All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver
@ 2020-09-18  1:07 Vladimir Oltean
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
                   ` (8 more replies)
  0 siblings, 9 replies; 33+ messages in thread
From: Vladimir Oltean @ 2020-09-18  1:07 UTC (permalink / raw)
  To: davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, f.fainelli, kuba

From: Vladimir Oltean <vladimir.oltean@nxp.com>

This is a series of 8 assorted patches for "net", on the drivers for the
VSC7514 MIPS switch (Ocelot-1), the VSC9953 PowerPC (Seville), and a few
more that are common to all supported devices since they are in the
common library portion.

Vladimir Oltean (8):
  net: mscc: ocelot: fix race condition with TX timestamping
  net: mscc: ocelot: add locking for the port TX timestamp ID
  net: dsa: seville: fix buffer size of the queue system
  net: mscc: ocelot: check for errors on memory allocation of ports
  net: mscc: ocelot: error checking when calling ocelot_init()
  net: mscc: ocelot: refactor ports parsing code into a dedicated
    function
  net: mscc: ocelot: unregister net devices on unbind
  net: mscc: ocelot: deinitialize only initialized ports

 drivers/net/dsa/ocelot/felix.c             |   8 +-
 drivers/net/dsa/ocelot/seville_vsc9953.c   |   2 +-
 drivers/net/ethernet/mscc/ocelot.c         |  24 ++-
 drivers/net/ethernet/mscc/ocelot_net.c     |  12 +-
 drivers/net/ethernet/mscc/ocelot_vsc7514.c | 233 ++++++++++++---------
 include/soc/mscc/ocelot.h                  |   2 +
 net/dsa/tag_ocelot.c                       |  11 +-
 7 files changed, 175 insertions(+), 117 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping
  2020-09-18  1:07 [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver Vladimir Oltean
@ 2020-09-18  1:07 ` Vladimir Oltean
  2020-09-18 15:23   ` Alexandre Belloni
                     ` (7 more replies)
  2020-09-18  1:07 ` [PATCH v2 net 2/8] net: mscc: ocelot: add locking for the port TX timestamp ID Vladimir Oltean
                   ` (7 subsequent siblings)
  8 siblings, 8 replies; 33+ messages in thread
From: Vladimir Oltean @ 2020-09-18  1:07 UTC (permalink / raw)
  To: davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, f.fainelli, kuba

From: Vladimir Oltean <vladimir.oltean@nxp.com>

The TX-timestampable skb is added late to the ocelot_port->tx_skbs. It
is in a race with the TX timestamp IRQ, which checks that queue trying
to match the timestamp with the skb by the ts_id. The skb should be
added to the queue before the IRQ can fire.

Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
Changes in v2:
None.

 drivers/net/ethernet/mscc/ocelot_net.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
index 0668d23cdbfa..cacabc23215a 100644
--- a/drivers/net/ethernet/mscc/ocelot_net.c
+++ b/drivers/net/ethernet/mscc/ocelot_net.c
@@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
 	u8 grp = 0; /* Send everything on CPU group 0 */
 	unsigned int i, count, last;
 	int port = priv->chip_port;
+	bool do_tstamp;
 
 	val = ocelot_read(ocelot, QS_INJ_STATUS);
 	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
@@ -344,10 +345,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
 	info.vid = skb_vlan_tag_get(skb);
 
 	/* Check if timestamping is needed */
+	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);
+
 	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
 		info.rew_op = ocelot_port->ptp_cmd;
-		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
+		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
 			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
+			ocelot_port->ts_id++;
+		}
 	}
 
 	ocelot_gen_ifh(ifh, &info);
@@ -380,12 +385,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
 	dev->stats.tx_packets++;
 	dev->stats.tx_bytes += skb->len;
 
-	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
-		ocelot_port->ts_id++;
-		return NETDEV_TX_OK;
-	}
+	if (!do_tstamp)
+		dev_kfree_skb_any(skb);
 
-	dev_kfree_skb_any(skb);
 	return NETDEV_TX_OK;
 }
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 net 2/8] net: mscc: ocelot: add locking for the port TX timestamp ID
  2020-09-18  1:07 [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver Vladimir Oltean
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
@ 2020-09-18  1:07 ` Vladimir Oltean
  2020-09-18  2:37   ` Florian Fainelli
  2020-09-18 15:27   ` Alexandre Belloni
  2020-09-18  1:07 ` [PATCH v2 net 3/8] net: dsa: seville: fix buffer size of the queue system Vladimir Oltean
                   ` (6 subsequent siblings)
  8 siblings, 2 replies; 33+ messages in thread
From: Vladimir Oltean @ 2020-09-18  1:07 UTC (permalink / raw)
  To: davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, f.fainelli, kuba

From: Vladimir Oltean <vladimir.oltean@nxp.com>

The ocelot_port->ts_id is used to:
(a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
    with an skb.
(b) populate the REW_OP from the injection header of the ongoing skb.
Only then is ocelot_port->ts_id incremented.

This is a problem because, at least theoretically, another timestampable
skb might use the same ocelot_port->ts_id before that is incremented.
Normally all transmit calls are serialized by the netdev transmit
spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
called by DSA, which has started declaring the NETIF_F_LLTX feature
since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
slave ports").  So the logic of using and incrementing the timestamp id
should be atomic per port.

The solution is to use the global ocelot_port->ts_id only while
protected by the associated ocelot_port->ts_id_lock. That's where we
populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
is called for the actual skb, but for felix, it is called for the skb's
clone. That is something which will also be changed in the future.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
Changes in v2:
Added an extra explanation about NETIF_F_LLTX in commit message.

 drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
 drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
 include/soc/mscc/ocelot.h              |  1 +
 net/dsa/tag_ocelot.c                   | 11 +++++++----
 4 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index 5abb7d2b0a9e..83eb7c325061 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
 
 	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
 	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
+		spin_lock(&ocelot_port->ts_id_lock);
+
 		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
 		/* Store timestamp ID in cb[0] of sk_buff */
-		skb->cb[0] = ocelot_port->ts_id % 4;
+		skb->cb[0] = ocelot_port->ts_id;
+		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
 		skb_queue_tail(&ocelot_port->tx_skbs, skb);
+
+		spin_unlock(&ocelot_port->ts_id_lock);
 		return 0;
 	}
 	return -ENODATA;
@@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
 	struct ocelot_port *ocelot_port = ocelot->ports[port];
 
 	skb_queue_head_init(&ocelot_port->tx_skbs);
+	spin_lock_init(&ocelot_port->ts_id_lock);
 
 	/* Basic L2 initialization */
 
diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
index cacabc23215a..8490e42e9e2d 100644
--- a/drivers/net/ethernet/mscc/ocelot_net.c
+++ b/drivers/net/ethernet/mscc/ocelot_net.c
@@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
 
 	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
 		info.rew_op = ocelot_port->ptp_cmd;
-		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
-			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
-			ocelot_port->ts_id++;
-		}
+		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
+			info.rew_op |= skb->cb[0] << 3;
 	}
 
 	ocelot_gen_ifh(ifh, &info);
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index da369b12005f..4521dd602ddc 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -566,6 +566,7 @@ struct ocelot_port {
 	u8				ptp_cmd;
 	struct sk_buff_head		tx_skbs;
 	u8				ts_id;
+	spinlock_t			ts_id_lock;
 
 	phy_interface_t			phy_mode;
 
diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
index 42f327c06dca..b4fc05cafaa6 100644
--- a/net/dsa/tag_ocelot.c
+++ b/net/dsa/tag_ocelot.c
@@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
 	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
 
 	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
+		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
+
 		rew_op = ocelot_port->ptp_cmd;
-		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
-			rew_op |= (ocelot_port->ts_id  % 4) << 3;
-			ocelot_port->ts_id++;
-		}
+		/* Retrieve timestamp ID populated inside skb->cb[0] of the
+		 * clone by ocelot_port_add_txtstamp_skb
+		 */
+		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
+			rew_op |= clone->cb[0] << 3;
 
 		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
 	}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 net 3/8] net: dsa: seville: fix buffer size of the queue system
  2020-09-18  1:07 [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver Vladimir Oltean
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
  2020-09-18  1:07 ` [PATCH v2 net 2/8] net: mscc: ocelot: add locking for the port TX timestamp ID Vladimir Oltean
@ 2020-09-18  1:07 ` Vladimir Oltean
  2020-09-18  2:37   ` Florian Fainelli
  2020-09-18 15:27   ` Alexandre Belloni
  2020-09-18  1:07 ` [PATCH v2 net 4/8] net: mscc: ocelot: check for errors on memory allocation of ports Vladimir Oltean
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 33+ messages in thread
From: Vladimir Oltean @ 2020-09-18  1:07 UTC (permalink / raw)
  To: davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, f.fainelli, kuba

From: Vladimir Oltean <vladimir.oltean@nxp.com>

The VSC9953 Seville switch has 2 megabits of buffer split into 4360
words of 60 bytes each.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
Changes in v2:
None.

 drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
index 2d6a5f5758f8..83a1ab9393e9 100644
--- a/drivers/net/dsa/ocelot/seville_vsc9953.c
+++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
@@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
 	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
 	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
 	.vcap			= vsc9953_vcap_props,
-	.shared_queue_sz	= 128 * 1024,
+	.shared_queue_sz	= 2048 * 1024,
 	.num_mact_rows		= 2048,
 	.num_ports		= 10,
 	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 net 4/8] net: mscc: ocelot: check for errors on memory allocation of ports
  2020-09-18  1:07 [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver Vladimir Oltean
                   ` (2 preceding siblings ...)
  2020-09-18  1:07 ` [PATCH v2 net 3/8] net: dsa: seville: fix buffer size of the queue system Vladimir Oltean
@ 2020-09-18  1:07 ` Vladimir Oltean
  2020-09-18  2:35   ` Florian Fainelli
  2020-09-18 15:27   ` Alexandre Belloni
  2020-09-18  1:07 ` [PATCH v2 net 5/8] net: mscc: ocelot: error checking when calling ocelot_init() Vladimir Oltean
                   ` (4 subsequent siblings)
  8 siblings, 2 replies; 33+ messages in thread
From: Vladimir Oltean @ 2020-09-18  1:07 UTC (permalink / raw)
  To: davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, f.fainelli, kuba

From: Vladimir Oltean <vladimir.oltean@nxp.com>

Do not proceed probing if we couldn't allocate memory for the ports
array, just error out.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
Changes in v2:
Stopped leaking the 'ports' OF node.

 drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index 65408bc994c4..904ea299a5e8 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
 
 	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
 				     sizeof(struct ocelot_port *), GFP_KERNEL);
+	if (!ocelot->ports) {
+		err = -ENOMEM;
+		goto out_put_ports;
+	}
 
 	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
 	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 net 5/8] net: mscc: ocelot: error checking when calling ocelot_init()
  2020-09-18  1:07 [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver Vladimir Oltean
                   ` (3 preceding siblings ...)
  2020-09-18  1:07 ` [PATCH v2 net 4/8] net: mscc: ocelot: check for errors on memory allocation of ports Vladimir Oltean
@ 2020-09-18  1:07 ` Vladimir Oltean
  2020-09-18  2:35   ` Florian Fainelli
  2020-09-18 15:28   ` Alexandre Belloni
  2020-09-18  1:07 ` [PATCH v2 net 6/8] net: mscc: ocelot: refactor ports parsing code into a dedicated function Vladimir Oltean
                   ` (3 subsequent siblings)
  8 siblings, 2 replies; 33+ messages in thread
From: Vladimir Oltean @ 2020-09-18  1:07 UTC (permalink / raw)
  To: davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, f.fainelli, kuba

From: Vladimir Oltean <vladimir.oltean@nxp.com>

ocelot_init() allocates memory, resets the switch and polls for a status
register, things which can fail. Stop probing the driver in that case,
and propagate the error result.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
Changes in v2:
Stopped leaking the 'ports' OF node in the VSC7514 driver.

 drivers/net/dsa/ocelot/felix.c             | 5 ++++-
 drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index a1e1d3824110..f7b43f8d56ed 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
 	if (err)
 		return err;
 
-	ocelot_init(ocelot);
+	err = ocelot_init(ocelot);
+	if (err)
+		return err;
+
 	if (ocelot->ptp) {
 		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
 		if (err) {
diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index 904ea299a5e8..a1cbb20a7757 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
 	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
 	ocelot->vcap = vsc7514_vcap_props;
 
-	ocelot_init(ocelot);
+	err = ocelot_init(ocelot);
+	if (err)
+		goto out_put_ports;
+
 	if (ocelot->ptp) {
 		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
 		if (err) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 net 6/8] net: mscc: ocelot: refactor ports parsing code into a dedicated function
  2020-09-18  1:07 [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver Vladimir Oltean
                   ` (4 preceding siblings ...)
  2020-09-18  1:07 ` [PATCH v2 net 5/8] net: mscc: ocelot: error checking when calling ocelot_init() Vladimir Oltean
@ 2020-09-18  1:07 ` Vladimir Oltean
  2020-09-18  2:34   ` Florian Fainelli
  2020-09-18 15:28   ` Alexandre Belloni
  2020-09-18  1:07 ` [PATCH v2 net 7/8] net: mscc: ocelot: unregister net devices on unbind Vladimir Oltean
                   ` (2 subsequent siblings)
  8 siblings, 2 replies; 33+ messages in thread
From: Vladimir Oltean @ 2020-09-18  1:07 UTC (permalink / raw)
  To: davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, f.fainelli, kuba

From: Vladimir Oltean <vladimir.oltean@nxp.com>

mscc_ocelot_probe() is already pretty large and hard to follow. So move
the code for parsing ports in a separate function.

This makes it easier for the next patch to just call
mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
Changes in v2:
Keep a reference to the 'ports' OF node at caller side, in
mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
early. The ocelot_init() function depends on it being set correctly.

 drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
 1 file changed, 110 insertions(+), 99 deletions(-)

diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index a1cbb20a7757..ff4a01424953 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
 	.enable		= ocelot_ptp_enable,
 };
 
+static int mscc_ocelot_init_ports(struct platform_device *pdev,
+				  struct device_node *ports)
+{
+	struct ocelot *ocelot = platform_get_drvdata(pdev);
+	struct device_node *portnp;
+	int err;
+
+	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
+				     sizeof(struct ocelot_port *), GFP_KERNEL);
+	if (!ocelot->ports)
+		return -ENOMEM;
+
+	/* No NPI port */
+	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
+			     OCELOT_TAG_PREFIX_NONE);
+
+	for_each_available_child_of_node(ports, portnp) {
+		struct ocelot_port_private *priv;
+		struct ocelot_port *ocelot_port;
+		struct device_node *phy_node;
+		phy_interface_t phy_mode;
+		struct phy_device *phy;
+		struct regmap *target;
+		struct resource *res;
+		struct phy *serdes;
+		char res_name[8];
+		u32 port;
+
+		if (of_property_read_u32(portnp, "reg", &port))
+			continue;
+
+		snprintf(res_name, sizeof(res_name), "port%d", port);
+
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						   res_name);
+		target = ocelot_regmap_init(ocelot, res);
+		if (IS_ERR(target))
+			continue;
+
+		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
+		if (!phy_node)
+			continue;
+
+		phy = of_phy_find_device(phy_node);
+		of_node_put(phy_node);
+		if (!phy)
+			continue;
+
+		err = ocelot_probe_port(ocelot, port, target, phy);
+		if (err) {
+			of_node_put(portnp);
+			return err;
+		}
+
+		ocelot_port = ocelot->ports[port];
+		priv = container_of(ocelot_port, struct ocelot_port_private,
+				    port);
+
+		of_get_phy_mode(portnp, &phy_mode);
+
+		ocelot_port->phy_mode = phy_mode;
+
+		switch (ocelot_port->phy_mode) {
+		case PHY_INTERFACE_MODE_NA:
+			continue;
+		case PHY_INTERFACE_MODE_SGMII:
+			break;
+		case PHY_INTERFACE_MODE_QSGMII:
+			/* Ensure clock signals and speed is set on all
+			 * QSGMII links
+			 */
+			ocelot_port_writel(ocelot_port,
+					   DEV_CLOCK_CFG_LINK_SPEED
+					   (OCELOT_SPEED_1000),
+					   DEV_CLOCK_CFG);
+			break;
+		default:
+			dev_err(ocelot->dev,
+				"invalid phy mode for port%d, (Q)SGMII only\n",
+				port);
+			of_node_put(portnp);
+			return -EINVAL;
+		}
+
+		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
+		if (IS_ERR(serdes)) {
+			err = PTR_ERR(serdes);
+			if (err == -EPROBE_DEFER)
+				dev_dbg(ocelot->dev, "deferring probe\n");
+			else
+				dev_err(ocelot->dev,
+					"missing SerDes phys for port%d\n",
+					port);
+
+			of_node_put(portnp);
+			return err;
+		}
+
+		priv->serdes = serdes;
+	}
+
+	return 0;
+}
+
 static int mscc_ocelot_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
-	struct device_node *ports, *portnp;
 	int err, irq_xtr, irq_ptp_rdy;
+	struct device_node *ports;
 	struct ocelot *ocelot;
 	struct regmap *hsio;
 	unsigned int i;
@@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
 
 	ports = of_get_child_by_name(np, "ethernet-ports");
 	if (!ports) {
-		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
+		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
 		return -ENODEV;
 	}
 
 	ocelot->num_phys_ports = of_get_child_count(ports);
 
-	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
-				     sizeof(struct ocelot_port *), GFP_KERNEL);
-	if (!ocelot->ports) {
-		err = -ENOMEM;
-		goto out_put_ports;
-	}
-
 	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
 	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
 	ocelot->vcap = vsc7514_vcap_props;
@@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
 	if (err)
 		goto out_put_ports;
 
+	err = mscc_ocelot_init_ports(pdev, ports);
+	if (err)
+		goto out_put_ports;
+
 	if (ocelot->ptp) {
 		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
 		if (err) {
@@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
 		}
 	}
 
-	/* No NPI port */
-	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
-			     OCELOT_TAG_PREFIX_NONE);
-
-	for_each_available_child_of_node(ports, portnp) {
-		struct ocelot_port_private *priv;
-		struct ocelot_port *ocelot_port;
-		struct device_node *phy_node;
-		phy_interface_t phy_mode;
-		struct phy_device *phy;
-		struct regmap *target;
-		struct resource *res;
-		struct phy *serdes;
-		char res_name[8];
-		u32 port;
-
-		if (of_property_read_u32(portnp, "reg", &port))
-			continue;
-
-		snprintf(res_name, sizeof(res_name), "port%d", port);
-
-		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-						   res_name);
-		target = ocelot_regmap_init(ocelot, res);
-		if (IS_ERR(target))
-			continue;
-
-		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
-		if (!phy_node)
-			continue;
-
-		phy = of_phy_find_device(phy_node);
-		of_node_put(phy_node);
-		if (!phy)
-			continue;
-
-		err = ocelot_probe_port(ocelot, port, target, phy);
-		if (err) {
-			of_node_put(portnp);
-			goto out_put_ports;
-		}
-
-		ocelot_port = ocelot->ports[port];
-		priv = container_of(ocelot_port, struct ocelot_port_private,
-				    port);
-
-		of_get_phy_mode(portnp, &phy_mode);
-
-		ocelot_port->phy_mode = phy_mode;
-
-		switch (ocelot_port->phy_mode) {
-		case PHY_INTERFACE_MODE_NA:
-			continue;
-		case PHY_INTERFACE_MODE_SGMII:
-			break;
-		case PHY_INTERFACE_MODE_QSGMII:
-			/* Ensure clock signals and speed is set on all
-			 * QSGMII links
-			 */
-			ocelot_port_writel(ocelot_port,
-					   DEV_CLOCK_CFG_LINK_SPEED
-					   (OCELOT_SPEED_1000),
-					   DEV_CLOCK_CFG);
-			break;
-		default:
-			dev_err(ocelot->dev,
-				"invalid phy mode for port%d, (Q)SGMII only\n",
-				port);
-			of_node_put(portnp);
-			err = -EINVAL;
-			goto out_put_ports;
-		}
-
-		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
-		if (IS_ERR(serdes)) {
-			err = PTR_ERR(serdes);
-			if (err == -EPROBE_DEFER)
-				dev_dbg(ocelot->dev, "deferring probe\n");
-			else
-				dev_err(ocelot->dev,
-					"missing SerDes phys for port%d\n",
-					port);
-
-			of_node_put(portnp);
-			goto out_put_ports;
-		}
-
-		priv->serdes = serdes;
-	}
-
 	register_netdevice_notifier(&ocelot_netdevice_nb);
 	register_switchdev_notifier(&ocelot_switchdev_nb);
 	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 net 7/8] net: mscc: ocelot: unregister net devices on unbind
  2020-09-18  1:07 [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver Vladimir Oltean
                   ` (5 preceding siblings ...)
  2020-09-18  1:07 ` [PATCH v2 net 6/8] net: mscc: ocelot: refactor ports parsing code into a dedicated function Vladimir Oltean
@ 2020-09-18  1:07 ` Vladimir Oltean
  2020-09-18  2:34   ` Florian Fainelli
  2020-09-18 15:28   ` Alexandre Belloni
  2020-09-18  1:07 ` [PATCH v2 net 8/8] net: mscc: ocelot: deinitialize only initialized ports Vladimir Oltean
  2020-09-18 20:52 ` [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver David Miller
  8 siblings, 2 replies; 33+ messages in thread
From: Vladimir Oltean @ 2020-09-18  1:07 UTC (permalink / raw)
  To: davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, f.fainelli, kuba

From: Vladimir Oltean <vladimir.oltean@nxp.com>

This driver was not unregistering its network interfaces on unbind.
Now it is.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
Changes in v2:
No longer call mscc_ocelot_release_ports from the regular exit path of
mscc_ocelot_init_ports, which was incorrect.

 drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index ff4a01424953..252c49b5f22b 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
 	.enable		= ocelot_ptp_enable,
 };
 
+static void mscc_ocelot_release_ports(struct ocelot *ocelot)
+{
+	int port;
+
+	for (port = 0; port < ocelot->num_phys_ports; port++) {
+		struct ocelot_port_private *priv;
+		struct ocelot_port *ocelot_port;
+
+		ocelot_port = ocelot->ports[port];
+		if (!ocelot_port)
+			continue;
+
+		priv = container_of(ocelot_port, struct ocelot_port_private,
+				    port);
+
+		unregister_netdev(priv->dev);
+		free_netdev(priv->dev);
+	}
+}
+
 static int mscc_ocelot_init_ports(struct platform_device *pdev,
 				  struct device_node *ports)
 {
@@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
 	struct ocelot *ocelot = platform_get_drvdata(pdev);
 
 	ocelot_deinit_timestamp(ocelot);
+	mscc_ocelot_release_ports(ocelot);
 	ocelot_deinit(ocelot);
 	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
 	unregister_switchdev_notifier(&ocelot_switchdev_nb);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 net 8/8] net: mscc: ocelot: deinitialize only initialized ports
  2020-09-18  1:07 [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver Vladimir Oltean
                   ` (6 preceding siblings ...)
  2020-09-18  1:07 ` [PATCH v2 net 7/8] net: mscc: ocelot: unregister net devices on unbind Vladimir Oltean
@ 2020-09-18  1:07 ` Vladimir Oltean
  2020-09-18  2:36   ` Florian Fainelli
  2020-09-18 15:29   ` Alexandre Belloni
  2020-09-18 20:52 ` [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver David Miller
  8 siblings, 2 replies; 33+ messages in thread
From: Vladimir Oltean @ 2020-09-18  1:07 UTC (permalink / raw)
  To: davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, f.fainelli, kuba

From: Vladimir Oltean <vladimir.oltean@nxp.com>

Currently mscc_ocelot_init_ports() will skip initializing a port when it
doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
NULL. Take this into consideration when tearing down the driver, and add
a new function ocelot_deinit_port() to the switch library, mirror of
ocelot_init_port(), which needs to be called by the driver for all ports
it has initialized.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v2:
Patch is new.

 drivers/net/dsa/ocelot/felix.c             |  3 +++
 drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
 drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
 include/soc/mscc/ocelot.h                  |  1 +
 4 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index f7b43f8d56ed..64939ee14648 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
 {
 	struct ocelot *ocelot = ds->priv;
 	struct felix *felix = ocelot_to_felix(ocelot);
+	int port;
 
 	if (felix->info->mdio_bus_free)
 		felix->info->mdio_bus_free(ocelot);
 
+	for (port = 0; port < ocelot->num_phys_ports; port++)
+		ocelot_deinit_port(ocelot, port);
 	ocelot_deinit_timestamp(ocelot);
 	/* stop workqueue thread */
 	ocelot_deinit(ocelot);
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index 83eb7c325061..8518e1d60da4 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
 
 void ocelot_deinit(struct ocelot *ocelot)
 {
-	struct ocelot_port *port;
-	int i;
-
 	cancel_delayed_work(&ocelot->stats_work);
 	destroy_workqueue(ocelot->stats_queue);
 	mutex_destroy(&ocelot->stats_lock);
-
-	for (i = 0; i < ocelot->num_phys_ports; i++) {
-		port = ocelot->ports[i];
-		skb_queue_purge(&port->tx_skbs);
-	}
 }
 EXPORT_SYMBOL(ocelot_deinit);
 
+void ocelot_deinit_port(struct ocelot *ocelot, int port)
+{
+	struct ocelot_port *ocelot_port = ocelot->ports[port];
+
+	skb_queue_purge(&ocelot_port->tx_skbs);
+}
+EXPORT_SYMBOL(ocelot_deinit_port);
+
 MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index 252c49b5f22b..e02fb8bfab63 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
 		if (!ocelot_port)
 			continue;
 
+		ocelot_deinit_port(ocelot, port);
+
 		priv = container_of(ocelot_port, struct ocelot_port_private,
 				    port);
 
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 4521dd602ddc..0ac4e7fba086 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
 int ocelot_init(struct ocelot *ocelot);
 void ocelot_deinit(struct ocelot *ocelot);
 void ocelot_init_port(struct ocelot *ocelot, int port);
+void ocelot_deinit_port(struct ocelot *ocelot, int port);
 
 /* DSA callbacks */
 void ocelot_port_enable(struct ocelot *ocelot, int port,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 7/8] net: mscc: ocelot: unregister net devices on unbind
  2020-09-18  1:07 ` [PATCH v2 net 7/8] net: mscc: ocelot: unregister net devices on unbind Vladimir Oltean
@ 2020-09-18  2:34   ` Florian Fainelli
  2020-09-18 15:28   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Florian Fainelli @ 2020-09-18  2:34 UTC (permalink / raw)
  To: Vladimir Oltean, davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, kuba



On 9/17/2020 6:07 PM, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This driver was not unregistering its network interfaces on unbind.
> Now it is.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 6/8] net: mscc: ocelot: refactor ports parsing code into a dedicated function
  2020-09-18  1:07 ` [PATCH v2 net 6/8] net: mscc: ocelot: refactor ports parsing code into a dedicated function Vladimir Oltean
@ 2020-09-18  2:34   ` Florian Fainelli
  2020-09-18 15:28   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Florian Fainelli @ 2020-09-18  2:34 UTC (permalink / raw)
  To: Vladimir Oltean, davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, kuba



On 9/17/2020 6:07 PM, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> mscc_ocelot_probe() is already pretty large and hard to follow. So move
> the code for parsing ports in a separate function.
> 
> This makes it easier for the next patch to just call
> mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 4/8] net: mscc: ocelot: check for errors on memory allocation of ports
  2020-09-18  1:07 ` [PATCH v2 net 4/8] net: mscc: ocelot: check for errors on memory allocation of ports Vladimir Oltean
@ 2020-09-18  2:35   ` Florian Fainelli
  2020-09-18 15:27   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Florian Fainelli @ 2020-09-18  2:35 UTC (permalink / raw)
  To: Vladimir Oltean, davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, kuba



On 9/17/2020 6:07 PM, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Do not proceed probing if we couldn't allocate memory for the ports
> array, just error out.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 5/8] net: mscc: ocelot: error checking when calling ocelot_init()
  2020-09-18  1:07 ` [PATCH v2 net 5/8] net: mscc: ocelot: error checking when calling ocelot_init() Vladimir Oltean
@ 2020-09-18  2:35   ` Florian Fainelli
  2020-09-18 15:28   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Florian Fainelli @ 2020-09-18  2:35 UTC (permalink / raw)
  To: Vladimir Oltean, davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, kuba



On 9/17/2020 6:07 PM, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> ocelot_init() allocates memory, resets the switch and polls for a status
> register, things which can fail. Stop probing the driver in that case,
> and propagate the error result.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 8/8] net: mscc: ocelot: deinitialize only initialized ports
  2020-09-18  1:07 ` [PATCH v2 net 8/8] net: mscc: ocelot: deinitialize only initialized ports Vladimir Oltean
@ 2020-09-18  2:36   ` Florian Fainelli
  2020-09-18 15:29   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Florian Fainelli @ 2020-09-18  2:36 UTC (permalink / raw)
  To: Vladimir Oltean, davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, kuba



On 9/17/2020 6:07 PM, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Currently mscc_ocelot_init_ports() will skip initializing a port when it
> doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> NULL. Take this into consideration when tearing down the driver, and add
> a new function ocelot_deinit_port() to the switch library, mirror of
> ocelot_init_port(), which needs to be called by the driver for all ports
> it has initialized.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 2/8] net: mscc: ocelot: add locking for the port TX timestamp ID
  2020-09-18  1:07 ` [PATCH v2 net 2/8] net: mscc: ocelot: add locking for the port TX timestamp ID Vladimir Oltean
@ 2020-09-18  2:37   ` Florian Fainelli
  2020-09-18 15:27   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Florian Fainelli @ 2020-09-18  2:37 UTC (permalink / raw)
  To: Vladimir Oltean, davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, kuba



On 9/17/2020 6:07 PM, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The ocelot_port->ts_id is used to:
> (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
>      with an skb.
> (b) populate the REW_OP from the injection header of the ongoing skb.
> Only then is ocelot_port->ts_id incremented.
> 
> This is a problem because, at least theoretically, another timestampable
> skb might use the same ocelot_port->ts_id before that is incremented.
> Normally all transmit calls are serialized by the netdev transmit
> spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> called by DSA, which has started declaring the NETIF_F_LLTX feature
> since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> slave ports").  So the logic of using and incrementing the timestamp id
> should be atomic per port.
> 
> The solution is to use the global ocelot_port->ts_id only while
> protected by the associated ocelot_port->ts_id_lock. That's where we
> populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> is called for the actual skb, but for felix, it is called for the skb's
> clone. That is something which will also be changed in the future.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 3/8] net: dsa: seville: fix buffer size of the queue system
  2020-09-18  1:07 ` [PATCH v2 net 3/8] net: dsa: seville: fix buffer size of the queue system Vladimir Oltean
@ 2020-09-18  2:37   ` Florian Fainelli
  2020-09-18 15:27   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Florian Fainelli @ 2020-09-18  2:37 UTC (permalink / raw)
  To: Vladimir Oltean, davem, netdev
  Cc: yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver, claudiu.manoil,
	alexandre.belloni, andrew, vivien.didelot, kuba



On 9/17/2020 6:07 PM, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> words of 60 bytes each.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
@ 2020-09-18 15:23   ` Alexandre Belloni
  2020-09-18 15:24   ` Alexandre Belloni
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:23 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:23+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The TX-timestampable skb is added late to the ocelot_port->tx_skbs. It
> is in a race with the TX timestamp IRQ, which checks that queue trying
> to match the timestamp with the skb by the ts_id. The skb should be
> added to the queue before the IRQ can fire.
> 
> Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> None.
> 
>  drivers/net/ethernet/mscc/ocelot_net.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index 0668d23cdbfa..cacabc23215a 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	u8 grp = 0; /* Send everything on CPU group 0 */
>  	unsigned int i, count, last;
>  	int port = priv->chip_port;
> +	bool do_tstamp;
>  
>  	val = ocelot_read(ocelot, QS_INJ_STATUS);
>  	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
> @@ -344,10 +345,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	info.vid = skb_vlan_tag_get(skb);
>  
>  	/* Check if timestamping is needed */
> +	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);
> +
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
>  			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> +			ocelot_port->ts_id++;
> +		}
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> @@ -380,12 +385,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	dev->stats.tx_packets++;
>  	dev->stats.tx_bytes += skb->len;
>  
> -	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
> -		ocelot_port->ts_id++;
> -		return NETDEV_TX_OK;
> -	}
> +	if (!do_tstamp)
> +		dev_kfree_skb_any(skb);
>  
> -	dev_kfree_skb_any(skb);
>  	return NETDEV_TX_OK;
>  }
>  
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:24+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The ocelot_port->ts_id is used to:
> (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
>     with an skb.
> (b) populate the REW_OP from the injection header of the ongoing skb.
> Only then is ocelot_port->ts_id incremented.
> 
> This is a problem because, at least theoretically, another timestampable
> skb might use the same ocelot_port->ts_id before that is incremented.
> Normally all transmit calls are serialized by the netdev transmit
> spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> called by DSA, which has started declaring the NETIF_F_LLTX feature
> since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> slave ports").  So the logic of using and incrementing the timestamp id
> should be atomic per port.
> 
> The solution is to use the global ocelot_port->ts_id only while
> protected by the associated ocelot_port->ts_id_lock. That's where we
> populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> is called for the actual skb, but for felix, it is called for the skb's
> clone. That is something which will also be changed in the future.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Added an extra explanation about NETIF_F_LLTX in commit message.
> 
>  drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
>  drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
>  include/soc/mscc/ocelot.h              |  1 +
>  net/dsa/tag_ocelot.c                   | 11 +++++++----
>  4 files changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 5abb7d2b0a9e..83eb7c325061 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
>  	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> +		spin_lock(&ocelot_port->ts_id_lock);
> +
>  		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
>  		/* Store timestamp ID in cb[0] of sk_buff */
> -		skb->cb[0] = ocelot_port->ts_id % 4;
> +		skb->cb[0] = ocelot_port->ts_id;
> +		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
>  		skb_queue_tail(&ocelot_port->tx_skbs, skb);
> +
> +		spin_unlock(&ocelot_port->ts_id_lock);
>  		return 0;
>  	}
>  	return -ENODATA;
> @@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
>  	struct ocelot_port *ocelot_port = ocelot->ports[port];
>  
>  	skb_queue_head_init(&ocelot_port->tx_skbs);
> +	spin_lock_init(&ocelot_port->ts_id_lock);
>  
>  	/* Basic L2 initialization */
>  
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index cacabc23215a..8490e42e9e2d 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			info.rew_op |= skb->cb[0] << 3;
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index da369b12005f..4521dd602ddc 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -566,6 +566,7 @@ struct ocelot_port {
>  	u8				ptp_cmd;
>  	struct sk_buff_head		tx_skbs;
>  	u8				ts_id;
> +	spinlock_t			ts_id_lock;
>  
>  	phy_interface_t			phy_mode;
>  
> diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
> index 42f327c06dca..b4fc05cafaa6 100644
> --- a/net/dsa/tag_ocelot.c
> +++ b/net/dsa/tag_ocelot.c
> @@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
>  	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
>  
>  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
> +		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
> +
>  		rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		/* Retrieve timestamp ID populated inside skb->cb[0] of the
> +		 * clone by ocelot_port_add_txtstamp_skb
> +		 */
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			rew_op |= clone->cb[0] << 3;
>  
>  		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
>  	}
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:25+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> words of 60 bytes each.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> None.
> 
>  drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 2d6a5f5758f8..83a1ab9393e9 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
>  	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
>  	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
>  	.vcap			= vsc9953_vcap_props,
> -	.shared_queue_sz	= 128 * 1024,
> +	.shared_queue_sz	= 2048 * 1024,
>  	.num_mact_rows		= 2048,
>  	.num_ports		= 10,
>  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:26+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Do not proceed probing if we couldn't allocate memory for the ports
> array, just error out.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 65408bc994c4..904ea299a5e8 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
>  				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports) {
> +		err = -ENOMEM;
> +		goto out_put_ports;
> +	}
>  
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:27+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> ocelot_init() allocates memory, resets the switch and polls for a status
> register, things which can fail. Stop probing the driver in that case,
> and propagate the error result.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node in the VSC7514 driver.
> 
>  drivers/net/dsa/ocelot/felix.c             | 5 ++++-
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index a1e1d3824110..f7b43f8d56ed 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
>  	if (err)
>  		return err;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		return err;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 904ea299a5e8..a1cbb20a7757 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:28+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> mscc_ocelot_probe() is already pretty large and hard to follow. So move
> the code for parsing ports in a separate function.
> 
> This makes it easier for the next patch to just call
> mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Keep a reference to the 'ports' OF node at caller side, in
> mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
> early. The ocelot_init() function depends on it being set correctly.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
>  1 file changed, 110 insertions(+), 99 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index a1cbb20a7757..ff4a01424953 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static int mscc_ocelot_init_ports(struct platform_device *pdev,
> +				  struct device_node *ports)
> +{
> +	struct ocelot *ocelot = platform_get_drvdata(pdev);
> +	struct device_node *portnp;
> +	int err;
> +
> +	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
> +				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports)
> +		return -ENOMEM;
> +
> +	/* No NPI port */
> +	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> +			     OCELOT_TAG_PREFIX_NONE);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +		struct device_node *phy_node;
> +		phy_interface_t phy_mode;
> +		struct phy_device *phy;
> +		struct regmap *target;
> +		struct resource *res;
> +		struct phy *serdes;
> +		char res_name[8];
> +		u32 port;
> +
> +		if (of_property_read_u32(portnp, "reg", &port))
> +			continue;
> +
> +		snprintf(res_name, sizeof(res_name), "port%d", port);
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						   res_name);
> +		target = ocelot_regmap_init(ocelot, res);
> +		if (IS_ERR(target))
> +			continue;
> +
> +		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> +		if (!phy_node)
> +			continue;
> +
> +		phy = of_phy_find_device(phy_node);
> +		of_node_put(phy_node);
> +		if (!phy)
> +			continue;
> +
> +		err = ocelot_probe_port(ocelot, port, target, phy);
> +		if (err) {
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		ocelot_port = ocelot->ports[port];
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		of_get_phy_mode(portnp, &phy_mode);
> +
> +		ocelot_port->phy_mode = phy_mode;
> +
> +		switch (ocelot_port->phy_mode) {
> +		case PHY_INTERFACE_MODE_NA:
> +			continue;
> +		case PHY_INTERFACE_MODE_SGMII:
> +			break;
> +		case PHY_INTERFACE_MODE_QSGMII:
> +			/* Ensure clock signals and speed is set on all
> +			 * QSGMII links
> +			 */
> +			ocelot_port_writel(ocelot_port,
> +					   DEV_CLOCK_CFG_LINK_SPEED
> +					   (OCELOT_SPEED_1000),
> +					   DEV_CLOCK_CFG);
> +			break;
> +		default:
> +			dev_err(ocelot->dev,
> +				"invalid phy mode for port%d, (Q)SGMII only\n",
> +				port);
> +			of_node_put(portnp);
> +			return -EINVAL;
> +		}
> +
> +		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err == -EPROBE_DEFER)
> +				dev_dbg(ocelot->dev, "deferring probe\n");
> +			else
> +				dev_err(ocelot->dev,
> +					"missing SerDes phys for port%d\n",
> +					port);
> +
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		priv->serdes = serdes;
> +	}
> +
> +	return 0;
> +}
> +
>  static int mscc_ocelot_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> -	struct device_node *ports, *portnp;
>  	int err, irq_xtr, irq_ptp_rdy;
> +	struct device_node *ports;
>  	struct ocelot *ocelot;
>  	struct regmap *hsio;
>  	unsigned int i;
> @@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ports = of_get_child_by_name(np, "ethernet-ports");
>  	if (!ports) {
> -		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
> +		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
>  		return -ENODEV;
>  	}
>  
>  	ocelot->num_phys_ports = of_get_child_count(ports);
>  
> -	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> -				     sizeof(struct ocelot_port *), GFP_KERNEL);
> -	if (!ocelot->ports) {
> -		err = -ENOMEM;
> -		goto out_put_ports;
> -	}
> -
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
> @@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	if (err)
>  		goto out_put_ports;
>  
> +	err = mscc_ocelot_init_ports(pdev, ports);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> @@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	/* No NPI port */
> -	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> -			     OCELOT_TAG_PREFIX_NONE);
> -
> -	for_each_available_child_of_node(ports, portnp) {
> -		struct ocelot_port_private *priv;
> -		struct ocelot_port *ocelot_port;
> -		struct device_node *phy_node;
> -		phy_interface_t phy_mode;
> -		struct phy_device *phy;
> -		struct regmap *target;
> -		struct resource *res;
> -		struct phy *serdes;
> -		char res_name[8];
> -		u32 port;
> -
> -		if (of_property_read_u32(portnp, "reg", &port))
> -			continue;
> -
> -		snprintf(res_name, sizeof(res_name), "port%d", port);
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> -						   res_name);
> -		target = ocelot_regmap_init(ocelot, res);
> -		if (IS_ERR(target))
> -			continue;
> -
> -		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> -		if (!phy_node)
> -			continue;
> -
> -		phy = of_phy_find_device(phy_node);
> -		of_node_put(phy_node);
> -		if (!phy)
> -			continue;
> -
> -		err = ocelot_probe_port(ocelot, port, target, phy);
> -		if (err) {
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		ocelot_port = ocelot->ports[port];
> -		priv = container_of(ocelot_port, struct ocelot_port_private,
> -				    port);
> -
> -		of_get_phy_mode(portnp, &phy_mode);
> -
> -		ocelot_port->phy_mode = phy_mode;
> -
> -		switch (ocelot_port->phy_mode) {
> -		case PHY_INTERFACE_MODE_NA:
> -			continue;
> -		case PHY_INTERFACE_MODE_SGMII:
> -			break;
> -		case PHY_INTERFACE_MODE_QSGMII:
> -			/* Ensure clock signals and speed is set on all
> -			 * QSGMII links
> -			 */
> -			ocelot_port_writel(ocelot_port,
> -					   DEV_CLOCK_CFG_LINK_SPEED
> -					   (OCELOT_SPEED_1000),
> -					   DEV_CLOCK_CFG);
> -			break;
> -		default:
> -			dev_err(ocelot->dev,
> -				"invalid phy mode for port%d, (Q)SGMII only\n",
> -				port);
> -			of_node_put(portnp);
> -			err = -EINVAL;
> -			goto out_put_ports;
> -		}
> -
> -		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> -		if (IS_ERR(serdes)) {
> -			err = PTR_ERR(serdes);
> -			if (err == -EPROBE_DEFER)
> -				dev_dbg(ocelot->dev, "deferring probe\n");
> -			else
> -				dev_err(ocelot->dev,
> -					"missing SerDes phys for port%d\n",
> -					port);
> -
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		priv->serdes = serdes;
> -	}
> -
>  	register_netdevice_notifier(&ocelot_netdevice_nb);
>  	register_switchdev_notifier(&ocelot_switchdev_nb);
>  	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:29+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This driver was not unregistering its network interfaces on unbind.
> Now it is.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> No longer call mscc_ocelot_release_ports from the regular exit path of
> mscc_ocelot_init_ports, which was incorrect.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index ff4a01424953..252c49b5f22b 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> +{
> +	int port;
> +
> +	for (port = 0; port < ocelot->num_phys_ports; port++) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +
> +		ocelot_port = ocelot->ports[port];
> +		if (!ocelot_port)
> +			continue;
> +
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		unregister_netdev(priv->dev);
> +		free_netdev(priv->dev);
> +	}
> +}
> +
>  static int mscc_ocelot_init_ports(struct platform_device *pdev,
>  				  struct device_node *ports)
>  {
> @@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
>  	struct ocelot *ocelot = platform_get_drvdata(pdev);
>  
>  	ocelot_deinit_timestamp(ocelot);
> +	mscc_ocelot_release_ports(ocelot);
>  	ocelot_deinit(ocelot);
>  	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
>  	unregister_switchdev_notifier(&ocelot_switchdev_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:30+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Currently mscc_ocelot_init_ports() will skip initializing a port when it
> doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> NULL. Take this into consideration when tearing down the driver, and add
> a new function ocelot_deinit_port() to the switch library, mirror of
> ocelot_init_port(), which needs to be called by the driver for all ports
> it has initialized.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Changes in v2:
> Patch is new.
> 
>  drivers/net/dsa/ocelot/felix.c             |  3 +++
>  drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
>  include/soc/mscc/ocelot.h                  |  1 +
>  4 files changed, 14 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index f7b43f8d56ed..64939ee14648 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
>  {
>  	struct ocelot *ocelot = ds->priv;
>  	struct felix *felix = ocelot_to_felix(ocelot);
> +	int port;
>  
>  	if (felix->info->mdio_bus_free)
>  		felix->info->mdio_bus_free(ocelot);
>  
> +	for (port = 0; port < ocelot->num_phys_ports; port++)
> +		ocelot_deinit_port(ocelot, port);
>  	ocelot_deinit_timestamp(ocelot);
>  	/* stop workqueue thread */
>  	ocelot_deinit(ocelot);
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 83eb7c325061..8518e1d60da4 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
>  
>  void ocelot_deinit(struct ocelot *ocelot)
>  {
> -	struct ocelot_port *port;
> -	int i;
> -
>  	cancel_delayed_work(&ocelot->stats_work);
>  	destroy_workqueue(ocelot->stats_queue);
>  	mutex_destroy(&ocelot->stats_lock);
> -
> -	for (i = 0; i < ocelot->num_phys_ports; i++) {
> -		port = ocelot->ports[i];
> -		skb_queue_purge(&port->tx_skbs);
> -	}
>  }
>  EXPORT_SYMBOL(ocelot_deinit);
>  
> +void ocelot_deinit_port(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_port *ocelot_port = ocelot->ports[port];
> +
> +	skb_queue_purge(&ocelot_port->tx_skbs);
> +}
> +EXPORT_SYMBOL(ocelot_deinit_port);
> +
>  MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 252c49b5f22b..e02fb8bfab63 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
>  		if (!ocelot_port)
>  			continue;
>  
> +		ocelot_deinit_port(ocelot, port);
> +
>  		priv = container_of(ocelot_port, struct ocelot_port_private,
>  				    port);
>  
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 4521dd602ddc..0ac4e7fba086 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
>  int ocelot_init(struct ocelot *ocelot);
>  void ocelot_deinit(struct ocelot *ocelot);
>  void ocelot_init_port(struct ocelot *ocelot, int port);
> +void ocelot_deinit_port(struct ocelot *ocelot, int port);
>  
>  /* DSA callbacks */
>  void ocelot_port_enable(struct ocelot *ocelot, int port,
> -- 
> 2.25.1
> 


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
  2020-09-18 15:23   ` Alexandre Belloni
@ 2020-09-18 15:24   ` Alexandre Belloni
  2020-09-18 15:24   ` Alexandre Belloni
                     ` (5 subsequent siblings)
  7 siblings, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:24 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:23+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The TX-timestampable skb is added late to the ocelot_port->tx_skbs. It
> is in a race with the TX timestamp IRQ, which checks that queue trying
> to match the timestamp with the skb by the ts_id. The skb should be
> added to the queue before the IRQ can fire.
> 
> Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> None.
> 
>  drivers/net/ethernet/mscc/ocelot_net.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index 0668d23cdbfa..cacabc23215a 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	u8 grp = 0; /* Send everything on CPU group 0 */
>  	unsigned int i, count, last;
>  	int port = priv->chip_port;
> +	bool do_tstamp;
>  
>  	val = ocelot_read(ocelot, QS_INJ_STATUS);
>  	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
> @@ -344,10 +345,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	info.vid = skb_vlan_tag_get(skb);
>  
>  	/* Check if timestamping is needed */
> +	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);
> +
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
>  			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> +			ocelot_port->ts_id++;
> +		}
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> @@ -380,12 +385,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	dev->stats.tx_packets++;
>  	dev->stats.tx_bytes += skb->len;
>  
> -	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
> -		ocelot_port->ts_id++;
> -		return NETDEV_TX_OK;
> -	}
> +	if (!do_tstamp)
> +		dev_kfree_skb_any(skb);
>  
> -	dev_kfree_skb_any(skb);
>  	return NETDEV_TX_OK;
>  }
>  
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:24+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The ocelot_port->ts_id is used to:
> (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
>     with an skb.
> (b) populate the REW_OP from the injection header of the ongoing skb.
> Only then is ocelot_port->ts_id incremented.
> 
> This is a problem because, at least theoretically, another timestampable
> skb might use the same ocelot_port->ts_id before that is incremented.
> Normally all transmit calls are serialized by the netdev transmit
> spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> called by DSA, which has started declaring the NETIF_F_LLTX feature
> since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> slave ports").  So the logic of using and incrementing the timestamp id
> should be atomic per port.
> 
> The solution is to use the global ocelot_port->ts_id only while
> protected by the associated ocelot_port->ts_id_lock. That's where we
> populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> is called for the actual skb, but for felix, it is called for the skb's
> clone. That is something which will also be changed in the future.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Added an extra explanation about NETIF_F_LLTX in commit message.
> 
>  drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
>  drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
>  include/soc/mscc/ocelot.h              |  1 +
>  net/dsa/tag_ocelot.c                   | 11 +++++++----
>  4 files changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 5abb7d2b0a9e..83eb7c325061 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
>  	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> +		spin_lock(&ocelot_port->ts_id_lock);
> +
>  		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
>  		/* Store timestamp ID in cb[0] of sk_buff */
> -		skb->cb[0] = ocelot_port->ts_id % 4;
> +		skb->cb[0] = ocelot_port->ts_id;
> +		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
>  		skb_queue_tail(&ocelot_port->tx_skbs, skb);
> +
> +		spin_unlock(&ocelot_port->ts_id_lock);
>  		return 0;
>  	}
>  	return -ENODATA;
> @@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
>  	struct ocelot_port *ocelot_port = ocelot->ports[port];
>  
>  	skb_queue_head_init(&ocelot_port->tx_skbs);
> +	spin_lock_init(&ocelot_port->ts_id_lock);
>  
>  	/* Basic L2 initialization */
>  
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index cacabc23215a..8490e42e9e2d 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			info.rew_op |= skb->cb[0] << 3;
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index da369b12005f..4521dd602ddc 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -566,6 +566,7 @@ struct ocelot_port {
>  	u8				ptp_cmd;
>  	struct sk_buff_head		tx_skbs;
>  	u8				ts_id;
> +	spinlock_t			ts_id_lock;
>  
>  	phy_interface_t			phy_mode;
>  
> diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
> index 42f327c06dca..b4fc05cafaa6 100644
> --- a/net/dsa/tag_ocelot.c
> +++ b/net/dsa/tag_ocelot.c
> @@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
>  	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
>  
>  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
> +		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
> +
>  		rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		/* Retrieve timestamp ID populated inside skb->cb[0] of the
> +		 * clone by ocelot_port_add_txtstamp_skb
> +		 */
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			rew_op |= clone->cb[0] << 3;
>  
>  		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
>  	}
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:25+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> words of 60 bytes each.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> None.
> 
>  drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 2d6a5f5758f8..83a1ab9393e9 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
>  	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
>  	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
>  	.vcap			= vsc9953_vcap_props,
> -	.shared_queue_sz	= 128 * 1024,
> +	.shared_queue_sz	= 2048 * 1024,
>  	.num_mact_rows		= 2048,
>  	.num_ports		= 10,
>  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:26+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Do not proceed probing if we couldn't allocate memory for the ports
> array, just error out.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 65408bc994c4..904ea299a5e8 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
>  				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports) {
> +		err = -ENOMEM;
> +		goto out_put_ports;
> +	}
>  
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:27+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> ocelot_init() allocates memory, resets the switch and polls for a status
> register, things which can fail. Stop probing the driver in that case,
> and propagate the error result.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node in the VSC7514 driver.
> 
>  drivers/net/dsa/ocelot/felix.c             | 5 ++++-
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index a1e1d3824110..f7b43f8d56ed 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
>  	if (err)
>  		return err;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		return err;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 904ea299a5e8..a1cbb20a7757 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:28+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> mscc_ocelot_probe() is already pretty large and hard to follow. So move
> the code for parsing ports in a separate function.
> 
> This makes it easier for the next patch to just call
> mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Keep a reference to the 'ports' OF node at caller side, in
> mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
> early. The ocelot_init() function depends on it being set correctly.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
>  1 file changed, 110 insertions(+), 99 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index a1cbb20a7757..ff4a01424953 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static int mscc_ocelot_init_ports(struct platform_device *pdev,
> +				  struct device_node *ports)
> +{
> +	struct ocelot *ocelot = platform_get_drvdata(pdev);
> +	struct device_node *portnp;
> +	int err;
> +
> +	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
> +				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports)
> +		return -ENOMEM;
> +
> +	/* No NPI port */
> +	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> +			     OCELOT_TAG_PREFIX_NONE);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +		struct device_node *phy_node;
> +		phy_interface_t phy_mode;
> +		struct phy_device *phy;
> +		struct regmap *target;
> +		struct resource *res;
> +		struct phy *serdes;
> +		char res_name[8];
> +		u32 port;
> +
> +		if (of_property_read_u32(portnp, "reg", &port))
> +			continue;
> +
> +		snprintf(res_name, sizeof(res_name), "port%d", port);
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						   res_name);
> +		target = ocelot_regmap_init(ocelot, res);
> +		if (IS_ERR(target))
> +			continue;
> +
> +		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> +		if (!phy_node)
> +			continue;
> +
> +		phy = of_phy_find_device(phy_node);
> +		of_node_put(phy_node);
> +		if (!phy)
> +			continue;
> +
> +		err = ocelot_probe_port(ocelot, port, target, phy);
> +		if (err) {
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		ocelot_port = ocelot->ports[port];
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		of_get_phy_mode(portnp, &phy_mode);
> +
> +		ocelot_port->phy_mode = phy_mode;
> +
> +		switch (ocelot_port->phy_mode) {
> +		case PHY_INTERFACE_MODE_NA:
> +			continue;
> +		case PHY_INTERFACE_MODE_SGMII:
> +			break;
> +		case PHY_INTERFACE_MODE_QSGMII:
> +			/* Ensure clock signals and speed is set on all
> +			 * QSGMII links
> +			 */
> +			ocelot_port_writel(ocelot_port,
> +					   DEV_CLOCK_CFG_LINK_SPEED
> +					   (OCELOT_SPEED_1000),
> +					   DEV_CLOCK_CFG);
> +			break;
> +		default:
> +			dev_err(ocelot->dev,
> +				"invalid phy mode for port%d, (Q)SGMII only\n",
> +				port);
> +			of_node_put(portnp);
> +			return -EINVAL;
> +		}
> +
> +		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err == -EPROBE_DEFER)
> +				dev_dbg(ocelot->dev, "deferring probe\n");
> +			else
> +				dev_err(ocelot->dev,
> +					"missing SerDes phys for port%d\n",
> +					port);
> +
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		priv->serdes = serdes;
> +	}
> +
> +	return 0;
> +}
> +
>  static int mscc_ocelot_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> -	struct device_node *ports, *portnp;
>  	int err, irq_xtr, irq_ptp_rdy;
> +	struct device_node *ports;
>  	struct ocelot *ocelot;
>  	struct regmap *hsio;
>  	unsigned int i;
> @@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ports = of_get_child_by_name(np, "ethernet-ports");
>  	if (!ports) {
> -		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
> +		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
>  		return -ENODEV;
>  	}
>  
>  	ocelot->num_phys_ports = of_get_child_count(ports);
>  
> -	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> -				     sizeof(struct ocelot_port *), GFP_KERNEL);
> -	if (!ocelot->ports) {
> -		err = -ENOMEM;
> -		goto out_put_ports;
> -	}
> -
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
> @@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	if (err)
>  		goto out_put_ports;
>  
> +	err = mscc_ocelot_init_ports(pdev, ports);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> @@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	/* No NPI port */
> -	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> -			     OCELOT_TAG_PREFIX_NONE);
> -
> -	for_each_available_child_of_node(ports, portnp) {
> -		struct ocelot_port_private *priv;
> -		struct ocelot_port *ocelot_port;
> -		struct device_node *phy_node;
> -		phy_interface_t phy_mode;
> -		struct phy_device *phy;
> -		struct regmap *target;
> -		struct resource *res;
> -		struct phy *serdes;
> -		char res_name[8];
> -		u32 port;
> -
> -		if (of_property_read_u32(portnp, "reg", &port))
> -			continue;
> -
> -		snprintf(res_name, sizeof(res_name), "port%d", port);
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> -						   res_name);
> -		target = ocelot_regmap_init(ocelot, res);
> -		if (IS_ERR(target))
> -			continue;
> -
> -		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> -		if (!phy_node)
> -			continue;
> -
> -		phy = of_phy_find_device(phy_node);
> -		of_node_put(phy_node);
> -		if (!phy)
> -			continue;
> -
> -		err = ocelot_probe_port(ocelot, port, target, phy);
> -		if (err) {
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		ocelot_port = ocelot->ports[port];
> -		priv = container_of(ocelot_port, struct ocelot_port_private,
> -				    port);
> -
> -		of_get_phy_mode(portnp, &phy_mode);
> -
> -		ocelot_port->phy_mode = phy_mode;
> -
> -		switch (ocelot_port->phy_mode) {
> -		case PHY_INTERFACE_MODE_NA:
> -			continue;
> -		case PHY_INTERFACE_MODE_SGMII:
> -			break;
> -		case PHY_INTERFACE_MODE_QSGMII:
> -			/* Ensure clock signals and speed is set on all
> -			 * QSGMII links
> -			 */
> -			ocelot_port_writel(ocelot_port,
> -					   DEV_CLOCK_CFG_LINK_SPEED
> -					   (OCELOT_SPEED_1000),
> -					   DEV_CLOCK_CFG);
> -			break;
> -		default:
> -			dev_err(ocelot->dev,
> -				"invalid phy mode for port%d, (Q)SGMII only\n",
> -				port);
> -			of_node_put(portnp);
> -			err = -EINVAL;
> -			goto out_put_ports;
> -		}
> -
> -		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> -		if (IS_ERR(serdes)) {
> -			err = PTR_ERR(serdes);
> -			if (err == -EPROBE_DEFER)
> -				dev_dbg(ocelot->dev, "deferring probe\n");
> -			else
> -				dev_err(ocelot->dev,
> -					"missing SerDes phys for port%d\n",
> -					port);
> -
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		priv->serdes = serdes;
> -	}
> -
>  	register_netdevice_notifier(&ocelot_netdevice_nb);
>  	register_switchdev_notifier(&ocelot_switchdev_nb);
>  	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:29+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This driver was not unregistering its network interfaces on unbind.
> Now it is.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> No longer call mscc_ocelot_release_ports from the regular exit path of
> mscc_ocelot_init_ports, which was incorrect.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index ff4a01424953..252c49b5f22b 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> +{
> +	int port;
> +
> +	for (port = 0; port < ocelot->num_phys_ports; port++) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +
> +		ocelot_port = ocelot->ports[port];
> +		if (!ocelot_port)
> +			continue;
> +
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		unregister_netdev(priv->dev);
> +		free_netdev(priv->dev);
> +	}
> +}
> +
>  static int mscc_ocelot_init_ports(struct platform_device *pdev,
>  				  struct device_node *ports)
>  {
> @@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
>  	struct ocelot *ocelot = platform_get_drvdata(pdev);
>  
>  	ocelot_deinit_timestamp(ocelot);
> +	mscc_ocelot_release_ports(ocelot);
>  	ocelot_deinit(ocelot);
>  	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
>  	unregister_switchdev_notifier(&ocelot_switchdev_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:30+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Currently mscc_ocelot_init_ports() will skip initializing a port when it
> doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> NULL. Take this into consideration when tearing down the driver, and add
> a new function ocelot_deinit_port() to the switch library, mirror of
> ocelot_init_port(), which needs to be called by the driver for all ports
> it has initialized.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Changes in v2:
> Patch is new.
> 
>  drivers/net/dsa/ocelot/felix.c             |  3 +++
>  drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
>  include/soc/mscc/ocelot.h                  |  1 +
>  4 files changed, 14 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index f7b43f8d56ed..64939ee14648 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
>  {
>  	struct ocelot *ocelot = ds->priv;
>  	struct felix *felix = ocelot_to_felix(ocelot);
> +	int port;
>  
>  	if (felix->info->mdio_bus_free)
>  		felix->info->mdio_bus_free(ocelot);
>  
> +	for (port = 0; port < ocelot->num_phys_ports; port++)
> +		ocelot_deinit_port(ocelot, port);
>  	ocelot_deinit_timestamp(ocelot);
>  	/* stop workqueue thread */
>  	ocelot_deinit(ocelot);
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 83eb7c325061..8518e1d60da4 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
>  
>  void ocelot_deinit(struct ocelot *ocelot)
>  {
> -	struct ocelot_port *port;
> -	int i;
> -
>  	cancel_delayed_work(&ocelot->stats_work);
>  	destroy_workqueue(ocelot->stats_queue);
>  	mutex_destroy(&ocelot->stats_lock);
> -
> -	for (i = 0; i < ocelot->num_phys_ports; i++) {
> -		port = ocelot->ports[i];
> -		skb_queue_purge(&port->tx_skbs);
> -	}
>  }
>  EXPORT_SYMBOL(ocelot_deinit);
>  
> +void ocelot_deinit_port(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_port *ocelot_port = ocelot->ports[port];
> +
> +	skb_queue_purge(&ocelot_port->tx_skbs);
> +}
> +EXPORT_SYMBOL(ocelot_deinit_port);
> +
>  MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 252c49b5f22b..e02fb8bfab63 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
>  		if (!ocelot_port)
>  			continue;
>  
> +		ocelot_deinit_port(ocelot, port);
> +
>  		priv = container_of(ocelot_port, struct ocelot_port_private,
>  				    port);
>  
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 4521dd602ddc..0ac4e7fba086 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
>  int ocelot_init(struct ocelot *ocelot);
>  void ocelot_deinit(struct ocelot *ocelot);
>  void ocelot_init_port(struct ocelot *ocelot, int port);
> +void ocelot_deinit_port(struct ocelot *ocelot, int port);
>  
>  /* DSA callbacks */
>  void ocelot_port_enable(struct ocelot *ocelot, int port,
> -- 
> 2.25.1
> 


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
  2020-09-18 15:23   ` Alexandre Belloni
  2020-09-18 15:24   ` Alexandre Belloni
@ 2020-09-18 15:24   ` Alexandre Belloni
  2020-09-18 15:24   ` Alexandre Belloni
                     ` (4 subsequent siblings)
  7 siblings, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:24 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:23+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The TX-timestampable skb is added late to the ocelot_port->tx_skbs. It
> is in a race with the TX timestamp IRQ, which checks that queue trying
> to match the timestamp with the skb by the ts_id. The skb should be
> added to the queue before the IRQ can fire.
> 
> Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> None.
> 
>  drivers/net/ethernet/mscc/ocelot_net.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index 0668d23cdbfa..cacabc23215a 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	u8 grp = 0; /* Send everything on CPU group 0 */
>  	unsigned int i, count, last;
>  	int port = priv->chip_port;
> +	bool do_tstamp;
>  
>  	val = ocelot_read(ocelot, QS_INJ_STATUS);
>  	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
> @@ -344,10 +345,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	info.vid = skb_vlan_tag_get(skb);
>  
>  	/* Check if timestamping is needed */
> +	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);
> +
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
>  			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> +			ocelot_port->ts_id++;
> +		}
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> @@ -380,12 +385,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	dev->stats.tx_packets++;
>  	dev->stats.tx_bytes += skb->len;
>  
> -	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
> -		ocelot_port->ts_id++;
> -		return NETDEV_TX_OK;
> -	}
> +	if (!do_tstamp)
> +		dev_kfree_skb_any(skb);
>  
> -	dev_kfree_skb_any(skb);
>  	return NETDEV_TX_OK;
>  }
>  
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:24+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The ocelot_port->ts_id is used to:
> (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
>     with an skb.
> (b) populate the REW_OP from the injection header of the ongoing skb.
> Only then is ocelot_port->ts_id incremented.
> 
> This is a problem because, at least theoretically, another timestampable
> skb might use the same ocelot_port->ts_id before that is incremented.
> Normally all transmit calls are serialized by the netdev transmit
> spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> called by DSA, which has started declaring the NETIF_F_LLTX feature
> since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> slave ports").  So the logic of using and incrementing the timestamp id
> should be atomic per port.
> 
> The solution is to use the global ocelot_port->ts_id only while
> protected by the associated ocelot_port->ts_id_lock. That's where we
> populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> is called for the actual skb, but for felix, it is called for the skb's
> clone. That is something which will also be changed in the future.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Added an extra explanation about NETIF_F_LLTX in commit message.
> 
>  drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
>  drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
>  include/soc/mscc/ocelot.h              |  1 +
>  net/dsa/tag_ocelot.c                   | 11 +++++++----
>  4 files changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 5abb7d2b0a9e..83eb7c325061 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
>  	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> +		spin_lock(&ocelot_port->ts_id_lock);
> +
>  		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
>  		/* Store timestamp ID in cb[0] of sk_buff */
> -		skb->cb[0] = ocelot_port->ts_id % 4;
> +		skb->cb[0] = ocelot_port->ts_id;
> +		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
>  		skb_queue_tail(&ocelot_port->tx_skbs, skb);
> +
> +		spin_unlock(&ocelot_port->ts_id_lock);
>  		return 0;
>  	}
>  	return -ENODATA;
> @@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
>  	struct ocelot_port *ocelot_port = ocelot->ports[port];
>  
>  	skb_queue_head_init(&ocelot_port->tx_skbs);
> +	spin_lock_init(&ocelot_port->ts_id_lock);
>  
>  	/* Basic L2 initialization */
>  
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index cacabc23215a..8490e42e9e2d 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			info.rew_op |= skb->cb[0] << 3;
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index da369b12005f..4521dd602ddc 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -566,6 +566,7 @@ struct ocelot_port {
>  	u8				ptp_cmd;
>  	struct sk_buff_head		tx_skbs;
>  	u8				ts_id;
> +	spinlock_t			ts_id_lock;
>  
>  	phy_interface_t			phy_mode;
>  
> diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
> index 42f327c06dca..b4fc05cafaa6 100644
> --- a/net/dsa/tag_ocelot.c
> +++ b/net/dsa/tag_ocelot.c
> @@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
>  	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
>  
>  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
> +		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
> +
>  		rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		/* Retrieve timestamp ID populated inside skb->cb[0] of the
> +		 * clone by ocelot_port_add_txtstamp_skb
> +		 */
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			rew_op |= clone->cb[0] << 3;
>  
>  		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
>  	}
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:25+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> words of 60 bytes each.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> None.
> 
>  drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 2d6a5f5758f8..83a1ab9393e9 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
>  	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
>  	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
>  	.vcap			= vsc9953_vcap_props,
> -	.shared_queue_sz	= 128 * 1024,
> +	.shared_queue_sz	= 2048 * 1024,
>  	.num_mact_rows		= 2048,
>  	.num_ports		= 10,
>  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:26+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Do not proceed probing if we couldn't allocate memory for the ports
> array, just error out.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 65408bc994c4..904ea299a5e8 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
>  				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports) {
> +		err = -ENOMEM;
> +		goto out_put_ports;
> +	}
>  
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:27+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> ocelot_init() allocates memory, resets the switch and polls for a status
> register, things which can fail. Stop probing the driver in that case,
> and propagate the error result.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node in the VSC7514 driver.
> 
>  drivers/net/dsa/ocelot/felix.c             | 5 ++++-
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index a1e1d3824110..f7b43f8d56ed 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
>  	if (err)
>  		return err;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		return err;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 904ea299a5e8..a1cbb20a7757 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:28+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> mscc_ocelot_probe() is already pretty large and hard to follow. So move
> the code for parsing ports in a separate function.
> 
> This makes it easier for the next patch to just call
> mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Keep a reference to the 'ports' OF node at caller side, in
> mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
> early. The ocelot_init() function depends on it being set correctly.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
>  1 file changed, 110 insertions(+), 99 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index a1cbb20a7757..ff4a01424953 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static int mscc_ocelot_init_ports(struct platform_device *pdev,
> +				  struct device_node *ports)
> +{
> +	struct ocelot *ocelot = platform_get_drvdata(pdev);
> +	struct device_node *portnp;
> +	int err;
> +
> +	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
> +				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports)
> +		return -ENOMEM;
> +
> +	/* No NPI port */
> +	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> +			     OCELOT_TAG_PREFIX_NONE);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +		struct device_node *phy_node;
> +		phy_interface_t phy_mode;
> +		struct phy_device *phy;
> +		struct regmap *target;
> +		struct resource *res;
> +		struct phy *serdes;
> +		char res_name[8];
> +		u32 port;
> +
> +		if (of_property_read_u32(portnp, "reg", &port))
> +			continue;
> +
> +		snprintf(res_name, sizeof(res_name), "port%d", port);
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						   res_name);
> +		target = ocelot_regmap_init(ocelot, res);
> +		if (IS_ERR(target))
> +			continue;
> +
> +		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> +		if (!phy_node)
> +			continue;
> +
> +		phy = of_phy_find_device(phy_node);
> +		of_node_put(phy_node);
> +		if (!phy)
> +			continue;
> +
> +		err = ocelot_probe_port(ocelot, port, target, phy);
> +		if (err) {
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		ocelot_port = ocelot->ports[port];
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		of_get_phy_mode(portnp, &phy_mode);
> +
> +		ocelot_port->phy_mode = phy_mode;
> +
> +		switch (ocelot_port->phy_mode) {
> +		case PHY_INTERFACE_MODE_NA:
> +			continue;
> +		case PHY_INTERFACE_MODE_SGMII:
> +			break;
> +		case PHY_INTERFACE_MODE_QSGMII:
> +			/* Ensure clock signals and speed is set on all
> +			 * QSGMII links
> +			 */
> +			ocelot_port_writel(ocelot_port,
> +					   DEV_CLOCK_CFG_LINK_SPEED
> +					   (OCELOT_SPEED_1000),
> +					   DEV_CLOCK_CFG);
> +			break;
> +		default:
> +			dev_err(ocelot->dev,
> +				"invalid phy mode for port%d, (Q)SGMII only\n",
> +				port);
> +			of_node_put(portnp);
> +			return -EINVAL;
> +		}
> +
> +		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err == -EPROBE_DEFER)
> +				dev_dbg(ocelot->dev, "deferring probe\n");
> +			else
> +				dev_err(ocelot->dev,
> +					"missing SerDes phys for port%d\n",
> +					port);
> +
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		priv->serdes = serdes;
> +	}
> +
> +	return 0;
> +}
> +
>  static int mscc_ocelot_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> -	struct device_node *ports, *portnp;
>  	int err, irq_xtr, irq_ptp_rdy;
> +	struct device_node *ports;
>  	struct ocelot *ocelot;
>  	struct regmap *hsio;
>  	unsigned int i;
> @@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ports = of_get_child_by_name(np, "ethernet-ports");
>  	if (!ports) {
> -		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
> +		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
>  		return -ENODEV;
>  	}
>  
>  	ocelot->num_phys_ports = of_get_child_count(ports);
>  
> -	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> -				     sizeof(struct ocelot_port *), GFP_KERNEL);
> -	if (!ocelot->ports) {
> -		err = -ENOMEM;
> -		goto out_put_ports;
> -	}
> -
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
> @@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	if (err)
>  		goto out_put_ports;
>  
> +	err = mscc_ocelot_init_ports(pdev, ports);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> @@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	/* No NPI port */
> -	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> -			     OCELOT_TAG_PREFIX_NONE);
> -
> -	for_each_available_child_of_node(ports, portnp) {
> -		struct ocelot_port_private *priv;
> -		struct ocelot_port *ocelot_port;
> -		struct device_node *phy_node;
> -		phy_interface_t phy_mode;
> -		struct phy_device *phy;
> -		struct regmap *target;
> -		struct resource *res;
> -		struct phy *serdes;
> -		char res_name[8];
> -		u32 port;
> -
> -		if (of_property_read_u32(portnp, "reg", &port))
> -			continue;
> -
> -		snprintf(res_name, sizeof(res_name), "port%d", port);
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> -						   res_name);
> -		target = ocelot_regmap_init(ocelot, res);
> -		if (IS_ERR(target))
> -			continue;
> -
> -		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> -		if (!phy_node)
> -			continue;
> -
> -		phy = of_phy_find_device(phy_node);
> -		of_node_put(phy_node);
> -		if (!phy)
> -			continue;
> -
> -		err = ocelot_probe_port(ocelot, port, target, phy);
> -		if (err) {
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		ocelot_port = ocelot->ports[port];
> -		priv = container_of(ocelot_port, struct ocelot_port_private,
> -				    port);
> -
> -		of_get_phy_mode(portnp, &phy_mode);
> -
> -		ocelot_port->phy_mode = phy_mode;
> -
> -		switch (ocelot_port->phy_mode) {
> -		case PHY_INTERFACE_MODE_NA:
> -			continue;
> -		case PHY_INTERFACE_MODE_SGMII:
> -			break;
> -		case PHY_INTERFACE_MODE_QSGMII:
> -			/* Ensure clock signals and speed is set on all
> -			 * QSGMII links
> -			 */
> -			ocelot_port_writel(ocelot_port,
> -					   DEV_CLOCK_CFG_LINK_SPEED
> -					   (OCELOT_SPEED_1000),
> -					   DEV_CLOCK_CFG);
> -			break;
> -		default:
> -			dev_err(ocelot->dev,
> -				"invalid phy mode for port%d, (Q)SGMII only\n",
> -				port);
> -			of_node_put(portnp);
> -			err = -EINVAL;
> -			goto out_put_ports;
> -		}
> -
> -		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> -		if (IS_ERR(serdes)) {
> -			err = PTR_ERR(serdes);
> -			if (err == -EPROBE_DEFER)
> -				dev_dbg(ocelot->dev, "deferring probe\n");
> -			else
> -				dev_err(ocelot->dev,
> -					"missing SerDes phys for port%d\n",
> -					port);
> -
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		priv->serdes = serdes;
> -	}
> -
>  	register_netdevice_notifier(&ocelot_netdevice_nb);
>  	register_switchdev_notifier(&ocelot_switchdev_nb);
>  	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:29+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This driver was not unregistering its network interfaces on unbind.
> Now it is.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> No longer call mscc_ocelot_release_ports from the regular exit path of
> mscc_ocelot_init_ports, which was incorrect.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index ff4a01424953..252c49b5f22b 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> +{
> +	int port;
> +
> +	for (port = 0; port < ocelot->num_phys_ports; port++) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +
> +		ocelot_port = ocelot->ports[port];
> +		if (!ocelot_port)
> +			continue;
> +
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		unregister_netdev(priv->dev);
> +		free_netdev(priv->dev);
> +	}
> +}
> +
>  static int mscc_ocelot_init_ports(struct platform_device *pdev,
>  				  struct device_node *ports)
>  {
> @@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
>  	struct ocelot *ocelot = platform_get_drvdata(pdev);
>  
>  	ocelot_deinit_timestamp(ocelot);
> +	mscc_ocelot_release_ports(ocelot);
>  	ocelot_deinit(ocelot);
>  	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
>  	unregister_switchdev_notifier(&ocelot_switchdev_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:30+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Currently mscc_ocelot_init_ports() will skip initializing a port when it
> doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> NULL. Take this into consideration when tearing down the driver, and add
> a new function ocelot_deinit_port() to the switch library, mirror of
> ocelot_init_port(), which needs to be called by the driver for all ports
> it has initialized.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Changes in v2:
> Patch is new.
> 
>  drivers/net/dsa/ocelot/felix.c             |  3 +++
>  drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
>  include/soc/mscc/ocelot.h                  |  1 +
>  4 files changed, 14 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index f7b43f8d56ed..64939ee14648 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
>  {
>  	struct ocelot *ocelot = ds->priv;
>  	struct felix *felix = ocelot_to_felix(ocelot);
> +	int port;
>  
>  	if (felix->info->mdio_bus_free)
>  		felix->info->mdio_bus_free(ocelot);
>  
> +	for (port = 0; port < ocelot->num_phys_ports; port++)
> +		ocelot_deinit_port(ocelot, port);
>  	ocelot_deinit_timestamp(ocelot);
>  	/* stop workqueue thread */
>  	ocelot_deinit(ocelot);
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 83eb7c325061..8518e1d60da4 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
>  
>  void ocelot_deinit(struct ocelot *ocelot)
>  {
> -	struct ocelot_port *port;
> -	int i;
> -
>  	cancel_delayed_work(&ocelot->stats_work);
>  	destroy_workqueue(ocelot->stats_queue);
>  	mutex_destroy(&ocelot->stats_lock);
> -
> -	for (i = 0; i < ocelot->num_phys_ports; i++) {
> -		port = ocelot->ports[i];
> -		skb_queue_purge(&port->tx_skbs);
> -	}
>  }
>  EXPORT_SYMBOL(ocelot_deinit);
>  
> +void ocelot_deinit_port(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_port *ocelot_port = ocelot->ports[port];
> +
> +	skb_queue_purge(&ocelot_port->tx_skbs);
> +}
> +EXPORT_SYMBOL(ocelot_deinit_port);
> +
>  MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 252c49b5f22b..e02fb8bfab63 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
>  		if (!ocelot_port)
>  			continue;
>  
> +		ocelot_deinit_port(ocelot, port);
> +
>  		priv = container_of(ocelot_port, struct ocelot_port_private,
>  				    port);
>  
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 4521dd602ddc..0ac4e7fba086 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
>  int ocelot_init(struct ocelot *ocelot);
>  void ocelot_deinit(struct ocelot *ocelot);
>  void ocelot_init_port(struct ocelot *ocelot, int port);
> +void ocelot_deinit_port(struct ocelot *ocelot, int port);
>  
>  /* DSA callbacks */
>  void ocelot_port_enable(struct ocelot *ocelot, int port,
> -- 
> 2.25.1
> 


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
                     ` (2 preceding siblings ...)
  2020-09-18 15:24   ` Alexandre Belloni
@ 2020-09-18 15:24   ` Alexandre Belloni
  2020-09-18 15:24   ` Alexandre Belloni
                     ` (3 subsequent siblings)
  7 siblings, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:24 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:23+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The TX-timestampable skb is added late to the ocelot_port->tx_skbs. It
> is in a race with the TX timestamp IRQ, which checks that queue trying
> to match the timestamp with the skb by the ts_id. The skb should be
> added to the queue before the IRQ can fire.
> 
> Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> None.
> 
>  drivers/net/ethernet/mscc/ocelot_net.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index 0668d23cdbfa..cacabc23215a 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	u8 grp = 0; /* Send everything on CPU group 0 */
>  	unsigned int i, count, last;
>  	int port = priv->chip_port;
> +	bool do_tstamp;
>  
>  	val = ocelot_read(ocelot, QS_INJ_STATUS);
>  	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
> @@ -344,10 +345,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	info.vid = skb_vlan_tag_get(skb);
>  
>  	/* Check if timestamping is needed */
> +	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);
> +
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
>  			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> +			ocelot_port->ts_id++;
> +		}
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> @@ -380,12 +385,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	dev->stats.tx_packets++;
>  	dev->stats.tx_bytes += skb->len;
>  
> -	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
> -		ocelot_port->ts_id++;
> -		return NETDEV_TX_OK;
> -	}
> +	if (!do_tstamp)
> +		dev_kfree_skb_any(skb);
>  
> -	dev_kfree_skb_any(skb);
>  	return NETDEV_TX_OK;
>  }
>  
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:24+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The ocelot_port->ts_id is used to:
> (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
>     with an skb.
> (b) populate the REW_OP from the injection header of the ongoing skb.
> Only then is ocelot_port->ts_id incremented.
> 
> This is a problem because, at least theoretically, another timestampable
> skb might use the same ocelot_port->ts_id before that is incremented.
> Normally all transmit calls are serialized by the netdev transmit
> spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> called by DSA, which has started declaring the NETIF_F_LLTX feature
> since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> slave ports").  So the logic of using and incrementing the timestamp id
> should be atomic per port.
> 
> The solution is to use the global ocelot_port->ts_id only while
> protected by the associated ocelot_port->ts_id_lock. That's where we
> populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> is called for the actual skb, but for felix, it is called for the skb's
> clone. That is something which will also be changed in the future.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Added an extra explanation about NETIF_F_LLTX in commit message.
> 
>  drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
>  drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
>  include/soc/mscc/ocelot.h              |  1 +
>  net/dsa/tag_ocelot.c                   | 11 +++++++----
>  4 files changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 5abb7d2b0a9e..83eb7c325061 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
>  	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> +		spin_lock(&ocelot_port->ts_id_lock);
> +
>  		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
>  		/* Store timestamp ID in cb[0] of sk_buff */
> -		skb->cb[0] = ocelot_port->ts_id % 4;
> +		skb->cb[0] = ocelot_port->ts_id;
> +		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
>  		skb_queue_tail(&ocelot_port->tx_skbs, skb);
> +
> +		spin_unlock(&ocelot_port->ts_id_lock);
>  		return 0;
>  	}
>  	return -ENODATA;
> @@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
>  	struct ocelot_port *ocelot_port = ocelot->ports[port];
>  
>  	skb_queue_head_init(&ocelot_port->tx_skbs);
> +	spin_lock_init(&ocelot_port->ts_id_lock);
>  
>  	/* Basic L2 initialization */
>  
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index cacabc23215a..8490e42e9e2d 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			info.rew_op |= skb->cb[0] << 3;
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index da369b12005f..4521dd602ddc 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -566,6 +566,7 @@ struct ocelot_port {
>  	u8				ptp_cmd;
>  	struct sk_buff_head		tx_skbs;
>  	u8				ts_id;
> +	spinlock_t			ts_id_lock;
>  
>  	phy_interface_t			phy_mode;
>  
> diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
> index 42f327c06dca..b4fc05cafaa6 100644
> --- a/net/dsa/tag_ocelot.c
> +++ b/net/dsa/tag_ocelot.c
> @@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
>  	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
>  
>  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
> +		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
> +
>  		rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		/* Retrieve timestamp ID populated inside skb->cb[0] of the
> +		 * clone by ocelot_port_add_txtstamp_skb
> +		 */
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			rew_op |= clone->cb[0] << 3;
>  
>  		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
>  	}
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:25+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> words of 60 bytes each.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> None.
> 
>  drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 2d6a5f5758f8..83a1ab9393e9 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
>  	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
>  	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
>  	.vcap			= vsc9953_vcap_props,
> -	.shared_queue_sz	= 128 * 1024,
> +	.shared_queue_sz	= 2048 * 1024,
>  	.num_mact_rows		= 2048,
>  	.num_ports		= 10,
>  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:26+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Do not proceed probing if we couldn't allocate memory for the ports
> array, just error out.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 65408bc994c4..904ea299a5e8 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
>  				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports) {
> +		err = -ENOMEM;
> +		goto out_put_ports;
> +	}
>  
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:27+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> ocelot_init() allocates memory, resets the switch and polls for a status
> register, things which can fail. Stop probing the driver in that case,
> and propagate the error result.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node in the VSC7514 driver.
> 
>  drivers/net/dsa/ocelot/felix.c             | 5 ++++-
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index a1e1d3824110..f7b43f8d56ed 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
>  	if (err)
>  		return err;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		return err;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 904ea299a5e8..a1cbb20a7757 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:28+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> mscc_ocelot_probe() is already pretty large and hard to follow. So move
> the code for parsing ports in a separate function.
> 
> This makes it easier for the next patch to just call
> mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Keep a reference to the 'ports' OF node at caller side, in
> mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
> early. The ocelot_init() function depends on it being set correctly.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
>  1 file changed, 110 insertions(+), 99 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index a1cbb20a7757..ff4a01424953 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static int mscc_ocelot_init_ports(struct platform_device *pdev,
> +				  struct device_node *ports)
> +{
> +	struct ocelot *ocelot = platform_get_drvdata(pdev);
> +	struct device_node *portnp;
> +	int err;
> +
> +	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
> +				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports)
> +		return -ENOMEM;
> +
> +	/* No NPI port */
> +	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> +			     OCELOT_TAG_PREFIX_NONE);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +		struct device_node *phy_node;
> +		phy_interface_t phy_mode;
> +		struct phy_device *phy;
> +		struct regmap *target;
> +		struct resource *res;
> +		struct phy *serdes;
> +		char res_name[8];
> +		u32 port;
> +
> +		if (of_property_read_u32(portnp, "reg", &port))
> +			continue;
> +
> +		snprintf(res_name, sizeof(res_name), "port%d", port);
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						   res_name);
> +		target = ocelot_regmap_init(ocelot, res);
> +		if (IS_ERR(target))
> +			continue;
> +
> +		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> +		if (!phy_node)
> +			continue;
> +
> +		phy = of_phy_find_device(phy_node);
> +		of_node_put(phy_node);
> +		if (!phy)
> +			continue;
> +
> +		err = ocelot_probe_port(ocelot, port, target, phy);
> +		if (err) {
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		ocelot_port = ocelot->ports[port];
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		of_get_phy_mode(portnp, &phy_mode);
> +
> +		ocelot_port->phy_mode = phy_mode;
> +
> +		switch (ocelot_port->phy_mode) {
> +		case PHY_INTERFACE_MODE_NA:
> +			continue;
> +		case PHY_INTERFACE_MODE_SGMII:
> +			break;
> +		case PHY_INTERFACE_MODE_QSGMII:
> +			/* Ensure clock signals and speed is set on all
> +			 * QSGMII links
> +			 */
> +			ocelot_port_writel(ocelot_port,
> +					   DEV_CLOCK_CFG_LINK_SPEED
> +					   (OCELOT_SPEED_1000),
> +					   DEV_CLOCK_CFG);
> +			break;
> +		default:
> +			dev_err(ocelot->dev,
> +				"invalid phy mode for port%d, (Q)SGMII only\n",
> +				port);
> +			of_node_put(portnp);
> +			return -EINVAL;
> +		}
> +
> +		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err == -EPROBE_DEFER)
> +				dev_dbg(ocelot->dev, "deferring probe\n");
> +			else
> +				dev_err(ocelot->dev,
> +					"missing SerDes phys for port%d\n",
> +					port);
> +
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		priv->serdes = serdes;
> +	}
> +
> +	return 0;
> +}
> +
>  static int mscc_ocelot_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> -	struct device_node *ports, *portnp;
>  	int err, irq_xtr, irq_ptp_rdy;
> +	struct device_node *ports;
>  	struct ocelot *ocelot;
>  	struct regmap *hsio;
>  	unsigned int i;
> @@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ports = of_get_child_by_name(np, "ethernet-ports");
>  	if (!ports) {
> -		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
> +		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
>  		return -ENODEV;
>  	}
>  
>  	ocelot->num_phys_ports = of_get_child_count(ports);
>  
> -	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> -				     sizeof(struct ocelot_port *), GFP_KERNEL);
> -	if (!ocelot->ports) {
> -		err = -ENOMEM;
> -		goto out_put_ports;
> -	}
> -
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
> @@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	if (err)
>  		goto out_put_ports;
>  
> +	err = mscc_ocelot_init_ports(pdev, ports);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> @@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	/* No NPI port */
> -	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> -			     OCELOT_TAG_PREFIX_NONE);
> -
> -	for_each_available_child_of_node(ports, portnp) {
> -		struct ocelot_port_private *priv;
> -		struct ocelot_port *ocelot_port;
> -		struct device_node *phy_node;
> -		phy_interface_t phy_mode;
> -		struct phy_device *phy;
> -		struct regmap *target;
> -		struct resource *res;
> -		struct phy *serdes;
> -		char res_name[8];
> -		u32 port;
> -
> -		if (of_property_read_u32(portnp, "reg", &port))
> -			continue;
> -
> -		snprintf(res_name, sizeof(res_name), "port%d", port);
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> -						   res_name);
> -		target = ocelot_regmap_init(ocelot, res);
> -		if (IS_ERR(target))
> -			continue;
> -
> -		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> -		if (!phy_node)
> -			continue;
> -
> -		phy = of_phy_find_device(phy_node);
> -		of_node_put(phy_node);
> -		if (!phy)
> -			continue;
> -
> -		err = ocelot_probe_port(ocelot, port, target, phy);
> -		if (err) {
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		ocelot_port = ocelot->ports[port];
> -		priv = container_of(ocelot_port, struct ocelot_port_private,
> -				    port);
> -
> -		of_get_phy_mode(portnp, &phy_mode);
> -
> -		ocelot_port->phy_mode = phy_mode;
> -
> -		switch (ocelot_port->phy_mode) {
> -		case PHY_INTERFACE_MODE_NA:
> -			continue;
> -		case PHY_INTERFACE_MODE_SGMII:
> -			break;
> -		case PHY_INTERFACE_MODE_QSGMII:
> -			/* Ensure clock signals and speed is set on all
> -			 * QSGMII links
> -			 */
> -			ocelot_port_writel(ocelot_port,
> -					   DEV_CLOCK_CFG_LINK_SPEED
> -					   (OCELOT_SPEED_1000),
> -					   DEV_CLOCK_CFG);
> -			break;
> -		default:
> -			dev_err(ocelot->dev,
> -				"invalid phy mode for port%d, (Q)SGMII only\n",
> -				port);
> -			of_node_put(portnp);
> -			err = -EINVAL;
> -			goto out_put_ports;
> -		}
> -
> -		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> -		if (IS_ERR(serdes)) {
> -			err = PTR_ERR(serdes);
> -			if (err == -EPROBE_DEFER)
> -				dev_dbg(ocelot->dev, "deferring probe\n");
> -			else
> -				dev_err(ocelot->dev,
> -					"missing SerDes phys for port%d\n",
> -					port);
> -
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		priv->serdes = serdes;
> -	}
> -
>  	register_netdevice_notifier(&ocelot_netdevice_nb);
>  	register_switchdev_notifier(&ocelot_switchdev_nb);
>  	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:29+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This driver was not unregistering its network interfaces on unbind.
> Now it is.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> No longer call mscc_ocelot_release_ports from the regular exit path of
> mscc_ocelot_init_ports, which was incorrect.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index ff4a01424953..252c49b5f22b 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> +{
> +	int port;
> +
> +	for (port = 0; port < ocelot->num_phys_ports; port++) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +
> +		ocelot_port = ocelot->ports[port];
> +		if (!ocelot_port)
> +			continue;
> +
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		unregister_netdev(priv->dev);
> +		free_netdev(priv->dev);
> +	}
> +}
> +
>  static int mscc_ocelot_init_ports(struct platform_device *pdev,
>  				  struct device_node *ports)
>  {
> @@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
>  	struct ocelot *ocelot = platform_get_drvdata(pdev);
>  
>  	ocelot_deinit_timestamp(ocelot);
> +	mscc_ocelot_release_ports(ocelot);
>  	ocelot_deinit(ocelot);
>  	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
>  	unregister_switchdev_notifier(&ocelot_switchdev_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:30+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Currently mscc_ocelot_init_ports() will skip initializing a port when it
> doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> NULL. Take this into consideration when tearing down the driver, and add
> a new function ocelot_deinit_port() to the switch library, mirror of
> ocelot_init_port(), which needs to be called by the driver for all ports
> it has initialized.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Changes in v2:
> Patch is new.
> 
>  drivers/net/dsa/ocelot/felix.c             |  3 +++
>  drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
>  include/soc/mscc/ocelot.h                  |  1 +
>  4 files changed, 14 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index f7b43f8d56ed..64939ee14648 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
>  {
>  	struct ocelot *ocelot = ds->priv;
>  	struct felix *felix = ocelot_to_felix(ocelot);
> +	int port;
>  
>  	if (felix->info->mdio_bus_free)
>  		felix->info->mdio_bus_free(ocelot);
>  
> +	for (port = 0; port < ocelot->num_phys_ports; port++)
> +		ocelot_deinit_port(ocelot, port);
>  	ocelot_deinit_timestamp(ocelot);
>  	/* stop workqueue thread */
>  	ocelot_deinit(ocelot);
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 83eb7c325061..8518e1d60da4 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
>  
>  void ocelot_deinit(struct ocelot *ocelot)
>  {
> -	struct ocelot_port *port;
> -	int i;
> -
>  	cancel_delayed_work(&ocelot->stats_work);
>  	destroy_workqueue(ocelot->stats_queue);
>  	mutex_destroy(&ocelot->stats_lock);
> -
> -	for (i = 0; i < ocelot->num_phys_ports; i++) {
> -		port = ocelot->ports[i];
> -		skb_queue_purge(&port->tx_skbs);
> -	}
>  }
>  EXPORT_SYMBOL(ocelot_deinit);
>  
> +void ocelot_deinit_port(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_port *ocelot_port = ocelot->ports[port];
> +
> +	skb_queue_purge(&ocelot_port->tx_skbs);
> +}
> +EXPORT_SYMBOL(ocelot_deinit_port);
> +
>  MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 252c49b5f22b..e02fb8bfab63 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
>  		if (!ocelot_port)
>  			continue;
>  
> +		ocelot_deinit_port(ocelot, port);
> +
>  		priv = container_of(ocelot_port, struct ocelot_port_private,
>  				    port);
>  
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 4521dd602ddc..0ac4e7fba086 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
>  int ocelot_init(struct ocelot *ocelot);
>  void ocelot_deinit(struct ocelot *ocelot);
>  void ocelot_init_port(struct ocelot *ocelot, int port);
> +void ocelot_deinit_port(struct ocelot *ocelot, int port);
>  
>  /* DSA callbacks */
>  void ocelot_port_enable(struct ocelot *ocelot, int port,
> -- 
> 2.25.1
> 


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
                     ` (3 preceding siblings ...)
  2020-09-18 15:24   ` Alexandre Belloni
@ 2020-09-18 15:24   ` Alexandre Belloni
  2020-09-18 15:25   ` Alexandre Belloni
                     ` (2 subsequent siblings)
  7 siblings, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:24 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:23+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The TX-timestampable skb is added late to the ocelot_port->tx_skbs. It
> is in a race with the TX timestamp IRQ, which checks that queue trying
> to match the timestamp with the skb by the ts_id. The skb should be
> added to the queue before the IRQ can fire.
> 
> Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> None.
> 
>  drivers/net/ethernet/mscc/ocelot_net.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index 0668d23cdbfa..cacabc23215a 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	u8 grp = 0; /* Send everything on CPU group 0 */
>  	unsigned int i, count, last;
>  	int port = priv->chip_port;
> +	bool do_tstamp;
>  
>  	val = ocelot_read(ocelot, QS_INJ_STATUS);
>  	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
> @@ -344,10 +345,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	info.vid = skb_vlan_tag_get(skb);
>  
>  	/* Check if timestamping is needed */
> +	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);
> +
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
>  			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> +			ocelot_port->ts_id++;
> +		}
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> @@ -380,12 +385,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	dev->stats.tx_packets++;
>  	dev->stats.tx_bytes += skb->len;
>  
> -	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
> -		ocelot_port->ts_id++;
> -		return NETDEV_TX_OK;
> -	}
> +	if (!do_tstamp)
> +		dev_kfree_skb_any(skb);
>  
> -	dev_kfree_skb_any(skb);
>  	return NETDEV_TX_OK;
>  }
>  
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:24+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The ocelot_port->ts_id is used to:
> (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
>     with an skb.
> (b) populate the REW_OP from the injection header of the ongoing skb.
> Only then is ocelot_port->ts_id incremented.
> 
> This is a problem because, at least theoretically, another timestampable
> skb might use the same ocelot_port->ts_id before that is incremented.
> Normally all transmit calls are serialized by the netdev transmit
> spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> called by DSA, which has started declaring the NETIF_F_LLTX feature
> since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> slave ports").  So the logic of using and incrementing the timestamp id
> should be atomic per port.
> 
> The solution is to use the global ocelot_port->ts_id only while
> protected by the associated ocelot_port->ts_id_lock. That's where we
> populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> is called for the actual skb, but for felix, it is called for the skb's
> clone. That is something which will also be changed in the future.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Added an extra explanation about NETIF_F_LLTX in commit message.
> 
>  drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
>  drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
>  include/soc/mscc/ocelot.h              |  1 +
>  net/dsa/tag_ocelot.c                   | 11 +++++++----
>  4 files changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 5abb7d2b0a9e..83eb7c325061 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
>  	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> +		spin_lock(&ocelot_port->ts_id_lock);
> +
>  		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
>  		/* Store timestamp ID in cb[0] of sk_buff */
> -		skb->cb[0] = ocelot_port->ts_id % 4;
> +		skb->cb[0] = ocelot_port->ts_id;
> +		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
>  		skb_queue_tail(&ocelot_port->tx_skbs, skb);
> +
> +		spin_unlock(&ocelot_port->ts_id_lock);
>  		return 0;
>  	}
>  	return -ENODATA;
> @@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
>  	struct ocelot_port *ocelot_port = ocelot->ports[port];
>  
>  	skb_queue_head_init(&ocelot_port->tx_skbs);
> +	spin_lock_init(&ocelot_port->ts_id_lock);
>  
>  	/* Basic L2 initialization */
>  
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index cacabc23215a..8490e42e9e2d 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			info.rew_op |= skb->cb[0] << 3;
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index da369b12005f..4521dd602ddc 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -566,6 +566,7 @@ struct ocelot_port {
>  	u8				ptp_cmd;
>  	struct sk_buff_head		tx_skbs;
>  	u8				ts_id;
> +	spinlock_t			ts_id_lock;
>  
>  	phy_interface_t			phy_mode;
>  
> diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
> index 42f327c06dca..b4fc05cafaa6 100644
> --- a/net/dsa/tag_ocelot.c
> +++ b/net/dsa/tag_ocelot.c
> @@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
>  	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
>  
>  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
> +		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
> +
>  		rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		/* Retrieve timestamp ID populated inside skb->cb[0] of the
> +		 * clone by ocelot_port_add_txtstamp_skb
> +		 */
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			rew_op |= clone->cb[0] << 3;
>  
>  		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
>  	}
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:25+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> words of 60 bytes each.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> None.
> 
>  drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 2d6a5f5758f8..83a1ab9393e9 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
>  	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
>  	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
>  	.vcap			= vsc9953_vcap_props,
> -	.shared_queue_sz	= 128 * 1024,
> +	.shared_queue_sz	= 2048 * 1024,
>  	.num_mact_rows		= 2048,
>  	.num_ports		= 10,
>  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:26+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Do not proceed probing if we couldn't allocate memory for the ports
> array, just error out.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 65408bc994c4..904ea299a5e8 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
>  				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports) {
> +		err = -ENOMEM;
> +		goto out_put_ports;
> +	}
>  
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:27+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> ocelot_init() allocates memory, resets the switch and polls for a status
> register, things which can fail. Stop probing the driver in that case,
> and propagate the error result.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node in the VSC7514 driver.
> 
>  drivers/net/dsa/ocelot/felix.c             | 5 ++++-
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index a1e1d3824110..f7b43f8d56ed 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
>  	if (err)
>  		return err;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		return err;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 904ea299a5e8..a1cbb20a7757 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:28+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> mscc_ocelot_probe() is already pretty large and hard to follow. So move
> the code for parsing ports in a separate function.
> 
> This makes it easier for the next patch to just call
> mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Keep a reference to the 'ports' OF node at caller side, in
> mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
> early. The ocelot_init() function depends on it being set correctly.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
>  1 file changed, 110 insertions(+), 99 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index a1cbb20a7757..ff4a01424953 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static int mscc_ocelot_init_ports(struct platform_device *pdev,
> +				  struct device_node *ports)
> +{
> +	struct ocelot *ocelot = platform_get_drvdata(pdev);
> +	struct device_node *portnp;
> +	int err;
> +
> +	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
> +				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports)
> +		return -ENOMEM;
> +
> +	/* No NPI port */
> +	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> +			     OCELOT_TAG_PREFIX_NONE);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +		struct device_node *phy_node;
> +		phy_interface_t phy_mode;
> +		struct phy_device *phy;
> +		struct regmap *target;
> +		struct resource *res;
> +		struct phy *serdes;
> +		char res_name[8];
> +		u32 port;
> +
> +		if (of_property_read_u32(portnp, "reg", &port))
> +			continue;
> +
> +		snprintf(res_name, sizeof(res_name), "port%d", port);
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						   res_name);
> +		target = ocelot_regmap_init(ocelot, res);
> +		if (IS_ERR(target))
> +			continue;
> +
> +		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> +		if (!phy_node)
> +			continue;
> +
> +		phy = of_phy_find_device(phy_node);
> +		of_node_put(phy_node);
> +		if (!phy)
> +			continue;
> +
> +		err = ocelot_probe_port(ocelot, port, target, phy);
> +		if (err) {
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		ocelot_port = ocelot->ports[port];
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		of_get_phy_mode(portnp, &phy_mode);
> +
> +		ocelot_port->phy_mode = phy_mode;
> +
> +		switch (ocelot_port->phy_mode) {
> +		case PHY_INTERFACE_MODE_NA:
> +			continue;
> +		case PHY_INTERFACE_MODE_SGMII:
> +			break;
> +		case PHY_INTERFACE_MODE_QSGMII:
> +			/* Ensure clock signals and speed is set on all
> +			 * QSGMII links
> +			 */
> +			ocelot_port_writel(ocelot_port,
> +					   DEV_CLOCK_CFG_LINK_SPEED
> +					   (OCELOT_SPEED_1000),
> +					   DEV_CLOCK_CFG);
> +			break;
> +		default:
> +			dev_err(ocelot->dev,
> +				"invalid phy mode for port%d, (Q)SGMII only\n",
> +				port);
> +			of_node_put(portnp);
> +			return -EINVAL;
> +		}
> +
> +		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err == -EPROBE_DEFER)
> +				dev_dbg(ocelot->dev, "deferring probe\n");
> +			else
> +				dev_err(ocelot->dev,
> +					"missing SerDes phys for port%d\n",
> +					port);
> +
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		priv->serdes = serdes;
> +	}
> +
> +	return 0;
> +}
> +
>  static int mscc_ocelot_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> -	struct device_node *ports, *portnp;
>  	int err, irq_xtr, irq_ptp_rdy;
> +	struct device_node *ports;
>  	struct ocelot *ocelot;
>  	struct regmap *hsio;
>  	unsigned int i;
> @@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ports = of_get_child_by_name(np, "ethernet-ports");
>  	if (!ports) {
> -		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
> +		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
>  		return -ENODEV;
>  	}
>  
>  	ocelot->num_phys_ports = of_get_child_count(ports);
>  
> -	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> -				     sizeof(struct ocelot_port *), GFP_KERNEL);
> -	if (!ocelot->ports) {
> -		err = -ENOMEM;
> -		goto out_put_ports;
> -	}
> -
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
> @@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	if (err)
>  		goto out_put_ports;
>  
> +	err = mscc_ocelot_init_ports(pdev, ports);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> @@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	/* No NPI port */
> -	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> -			     OCELOT_TAG_PREFIX_NONE);
> -
> -	for_each_available_child_of_node(ports, portnp) {
> -		struct ocelot_port_private *priv;
> -		struct ocelot_port *ocelot_port;
> -		struct device_node *phy_node;
> -		phy_interface_t phy_mode;
> -		struct phy_device *phy;
> -		struct regmap *target;
> -		struct resource *res;
> -		struct phy *serdes;
> -		char res_name[8];
> -		u32 port;
> -
> -		if (of_property_read_u32(portnp, "reg", &port))
> -			continue;
> -
> -		snprintf(res_name, sizeof(res_name), "port%d", port);
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> -						   res_name);
> -		target = ocelot_regmap_init(ocelot, res);
> -		if (IS_ERR(target))
> -			continue;
> -
> -		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> -		if (!phy_node)
> -			continue;
> -
> -		phy = of_phy_find_device(phy_node);
> -		of_node_put(phy_node);
> -		if (!phy)
> -			continue;
> -
> -		err = ocelot_probe_port(ocelot, port, target, phy);
> -		if (err) {
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		ocelot_port = ocelot->ports[port];
> -		priv = container_of(ocelot_port, struct ocelot_port_private,
> -				    port);
> -
> -		of_get_phy_mode(portnp, &phy_mode);
> -
> -		ocelot_port->phy_mode = phy_mode;
> -
> -		switch (ocelot_port->phy_mode) {
> -		case PHY_INTERFACE_MODE_NA:
> -			continue;
> -		case PHY_INTERFACE_MODE_SGMII:
> -			break;
> -		case PHY_INTERFACE_MODE_QSGMII:
> -			/* Ensure clock signals and speed is set on all
> -			 * QSGMII links
> -			 */
> -			ocelot_port_writel(ocelot_port,
> -					   DEV_CLOCK_CFG_LINK_SPEED
> -					   (OCELOT_SPEED_1000),
> -					   DEV_CLOCK_CFG);
> -			break;
> -		default:
> -			dev_err(ocelot->dev,
> -				"invalid phy mode for port%d, (Q)SGMII only\n",
> -				port);
> -			of_node_put(portnp);
> -			err = -EINVAL;
> -			goto out_put_ports;
> -		}
> -
> -		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> -		if (IS_ERR(serdes)) {
> -			err = PTR_ERR(serdes);
> -			if (err == -EPROBE_DEFER)
> -				dev_dbg(ocelot->dev, "deferring probe\n");
> -			else
> -				dev_err(ocelot->dev,
> -					"missing SerDes phys for port%d\n",
> -					port);
> -
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		priv->serdes = serdes;
> -	}
> -
>  	register_netdevice_notifier(&ocelot_netdevice_nb);
>  	register_switchdev_notifier(&ocelot_switchdev_nb);
>  	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:29+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This driver was not unregistering its network interfaces on unbind.
> Now it is.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> No longer call mscc_ocelot_release_ports from the regular exit path of
> mscc_ocelot_init_ports, which was incorrect.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index ff4a01424953..252c49b5f22b 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> +{
> +	int port;
> +
> +	for (port = 0; port < ocelot->num_phys_ports; port++) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +
> +		ocelot_port = ocelot->ports[port];
> +		if (!ocelot_port)
> +			continue;
> +
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		unregister_netdev(priv->dev);
> +		free_netdev(priv->dev);
> +	}
> +}
> +
>  static int mscc_ocelot_init_ports(struct platform_device *pdev,
>  				  struct device_node *ports)
>  {
> @@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
>  	struct ocelot *ocelot = platform_get_drvdata(pdev);
>  
>  	ocelot_deinit_timestamp(ocelot);
> +	mscc_ocelot_release_ports(ocelot);
>  	ocelot_deinit(ocelot);
>  	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
>  	unregister_switchdev_notifier(&ocelot_switchdev_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:30+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Currently mscc_ocelot_init_ports() will skip initializing a port when it
> doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> NULL. Take this into consideration when tearing down the driver, and add
> a new function ocelot_deinit_port() to the switch library, mirror of
> ocelot_init_port(), which needs to be called by the driver for all ports
> it has initialized.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Changes in v2:
> Patch is new.
> 
>  drivers/net/dsa/ocelot/felix.c             |  3 +++
>  drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
>  include/soc/mscc/ocelot.h                  |  1 +
>  4 files changed, 14 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index f7b43f8d56ed..64939ee14648 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
>  {
>  	struct ocelot *ocelot = ds->priv;
>  	struct felix *felix = ocelot_to_felix(ocelot);
> +	int port;
>  
>  	if (felix->info->mdio_bus_free)
>  		felix->info->mdio_bus_free(ocelot);
>  
> +	for (port = 0; port < ocelot->num_phys_ports; port++)
> +		ocelot_deinit_port(ocelot, port);
>  	ocelot_deinit_timestamp(ocelot);
>  	/* stop workqueue thread */
>  	ocelot_deinit(ocelot);
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 83eb7c325061..8518e1d60da4 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
>  
>  void ocelot_deinit(struct ocelot *ocelot)
>  {
> -	struct ocelot_port *port;
> -	int i;
> -
>  	cancel_delayed_work(&ocelot->stats_work);
>  	destroy_workqueue(ocelot->stats_queue);
>  	mutex_destroy(&ocelot->stats_lock);
> -
> -	for (i = 0; i < ocelot->num_phys_ports; i++) {
> -		port = ocelot->ports[i];
> -		skb_queue_purge(&port->tx_skbs);
> -	}
>  }
>  EXPORT_SYMBOL(ocelot_deinit);
>  
> +void ocelot_deinit_port(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_port *ocelot_port = ocelot->ports[port];
> +
> +	skb_queue_purge(&ocelot_port->tx_skbs);
> +}
> +EXPORT_SYMBOL(ocelot_deinit_port);
> +
>  MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 252c49b5f22b..e02fb8bfab63 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
>  		if (!ocelot_port)
>  			continue;
>  
> +		ocelot_deinit_port(ocelot, port);
> +
>  		priv = container_of(ocelot_port, struct ocelot_port_private,
>  				    port);
>  
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 4521dd602ddc..0ac4e7fba086 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
>  int ocelot_init(struct ocelot *ocelot);
>  void ocelot_deinit(struct ocelot *ocelot);
>  void ocelot_init_port(struct ocelot *ocelot, int port);
> +void ocelot_deinit_port(struct ocelot *ocelot, int port);
>  
>  /* DSA callbacks */
>  void ocelot_port_enable(struct ocelot *ocelot, int port,
> -- 
> 2.25.1
> 


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
                     ` (4 preceding siblings ...)
  2020-09-18 15:24   ` Alexandre Belloni
@ 2020-09-18 15:25   ` Alexandre Belloni
  2020-09-18 15:25   ` Alexandre Belloni
  2020-09-18 15:26   ` Alexandre Belloni
  7 siblings, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:25 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:23+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The TX-timestampable skb is added late to the ocelot_port->tx_skbs. It
> is in a race with the TX timestamp IRQ, which checks that queue trying
> to match the timestamp with the skb by the ts_id. The skb should be
> added to the queue before the IRQ can fire.
> 
> Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> None.
> 
>  drivers/net/ethernet/mscc/ocelot_net.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index 0668d23cdbfa..cacabc23215a 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	u8 grp = 0; /* Send everything on CPU group 0 */
>  	unsigned int i, count, last;
>  	int port = priv->chip_port;
> +	bool do_tstamp;
>  
>  	val = ocelot_read(ocelot, QS_INJ_STATUS);
>  	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
> @@ -344,10 +345,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	info.vid = skb_vlan_tag_get(skb);
>  
>  	/* Check if timestamping is needed */
> +	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);
> +
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
>  			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> +			ocelot_port->ts_id++;
> +		}
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> @@ -380,12 +385,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	dev->stats.tx_packets++;
>  	dev->stats.tx_bytes += skb->len;
>  
> -	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
> -		ocelot_port->ts_id++;
> -		return NETDEV_TX_OK;
> -	}
> +	if (!do_tstamp)
> +		dev_kfree_skb_any(skb);
>  
> -	dev_kfree_skb_any(skb);
>  	return NETDEV_TX_OK;
>  }
>  
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:24+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The ocelot_port->ts_id is used to:
> (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
>     with an skb.
> (b) populate the REW_OP from the injection header of the ongoing skb.
> Only then is ocelot_port->ts_id incremented.
> 
> This is a problem because, at least theoretically, another timestampable
> skb might use the same ocelot_port->ts_id before that is incremented.
> Normally all transmit calls are serialized by the netdev transmit
> spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> called by DSA, which has started declaring the NETIF_F_LLTX feature
> since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> slave ports").  So the logic of using and incrementing the timestamp id
> should be atomic per port.
> 
> The solution is to use the global ocelot_port->ts_id only while
> protected by the associated ocelot_port->ts_id_lock. That's where we
> populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> is called for the actual skb, but for felix, it is called for the skb's
> clone. That is something which will also be changed in the future.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Added an extra explanation about NETIF_F_LLTX in commit message.
> 
>  drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
>  drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
>  include/soc/mscc/ocelot.h              |  1 +
>  net/dsa/tag_ocelot.c                   | 11 +++++++----
>  4 files changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 5abb7d2b0a9e..83eb7c325061 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
>  	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> +		spin_lock(&ocelot_port->ts_id_lock);
> +
>  		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
>  		/* Store timestamp ID in cb[0] of sk_buff */
> -		skb->cb[0] = ocelot_port->ts_id % 4;
> +		skb->cb[0] = ocelot_port->ts_id;
> +		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
>  		skb_queue_tail(&ocelot_port->tx_skbs, skb);
> +
> +		spin_unlock(&ocelot_port->ts_id_lock);
>  		return 0;
>  	}
>  	return -ENODATA;
> @@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
>  	struct ocelot_port *ocelot_port = ocelot->ports[port];
>  
>  	skb_queue_head_init(&ocelot_port->tx_skbs);
> +	spin_lock_init(&ocelot_port->ts_id_lock);
>  
>  	/* Basic L2 initialization */
>  
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index cacabc23215a..8490e42e9e2d 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			info.rew_op |= skb->cb[0] << 3;
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index da369b12005f..4521dd602ddc 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -566,6 +566,7 @@ struct ocelot_port {
>  	u8				ptp_cmd;
>  	struct sk_buff_head		tx_skbs;
>  	u8				ts_id;
> +	spinlock_t			ts_id_lock;
>  
>  	phy_interface_t			phy_mode;
>  
> diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
> index 42f327c06dca..b4fc05cafaa6 100644
> --- a/net/dsa/tag_ocelot.c
> +++ b/net/dsa/tag_ocelot.c
> @@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
>  	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
>  
>  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
> +		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
> +
>  		rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		/* Retrieve timestamp ID populated inside skb->cb[0] of the
> +		 * clone by ocelot_port_add_txtstamp_skb
> +		 */
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			rew_op |= clone->cb[0] << 3;
>  
>  		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
>  	}
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:25+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> words of 60 bytes each.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> None.
> 
>  drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 2d6a5f5758f8..83a1ab9393e9 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
>  	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
>  	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
>  	.vcap			= vsc9953_vcap_props,
> -	.shared_queue_sz	= 128 * 1024,
> +	.shared_queue_sz	= 2048 * 1024,
>  	.num_mact_rows		= 2048,
>  	.num_ports		= 10,
>  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:26+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Do not proceed probing if we couldn't allocate memory for the ports
> array, just error out.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 65408bc994c4..904ea299a5e8 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
>  				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports) {
> +		err = -ENOMEM;
> +		goto out_put_ports;
> +	}
>  
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:27+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> ocelot_init() allocates memory, resets the switch and polls for a status
> register, things which can fail. Stop probing the driver in that case,
> and propagate the error result.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node in the VSC7514 driver.
> 
>  drivers/net/dsa/ocelot/felix.c             | 5 ++++-
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index a1e1d3824110..f7b43f8d56ed 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
>  	if (err)
>  		return err;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		return err;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 904ea299a5e8..a1cbb20a7757 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:28+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> mscc_ocelot_probe() is already pretty large and hard to follow. So move
> the code for parsing ports in a separate function.
> 
> This makes it easier for the next patch to just call
> mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Keep a reference to the 'ports' OF node at caller side, in
> mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
> early. The ocelot_init() function depends on it being set correctly.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
>  1 file changed, 110 insertions(+), 99 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index a1cbb20a7757..ff4a01424953 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static int mscc_ocelot_init_ports(struct platform_device *pdev,
> +				  struct device_node *ports)
> +{
> +	struct ocelot *ocelot = platform_get_drvdata(pdev);
> +	struct device_node *portnp;
> +	int err;
> +
> +	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
> +				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports)
> +		return -ENOMEM;
> +
> +	/* No NPI port */
> +	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> +			     OCELOT_TAG_PREFIX_NONE);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +		struct device_node *phy_node;
> +		phy_interface_t phy_mode;
> +		struct phy_device *phy;
> +		struct regmap *target;
> +		struct resource *res;
> +		struct phy *serdes;
> +		char res_name[8];
> +		u32 port;
> +
> +		if (of_property_read_u32(portnp, "reg", &port))
> +			continue;
> +
> +		snprintf(res_name, sizeof(res_name), "port%d", port);
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						   res_name);
> +		target = ocelot_regmap_init(ocelot, res);
> +		if (IS_ERR(target))
> +			continue;
> +
> +		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> +		if (!phy_node)
> +			continue;
> +
> +		phy = of_phy_find_device(phy_node);
> +		of_node_put(phy_node);
> +		if (!phy)
> +			continue;
> +
> +		err = ocelot_probe_port(ocelot, port, target, phy);
> +		if (err) {
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		ocelot_port = ocelot->ports[port];
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		of_get_phy_mode(portnp, &phy_mode);
> +
> +		ocelot_port->phy_mode = phy_mode;
> +
> +		switch (ocelot_port->phy_mode) {
> +		case PHY_INTERFACE_MODE_NA:
> +			continue;
> +		case PHY_INTERFACE_MODE_SGMII:
> +			break;
> +		case PHY_INTERFACE_MODE_QSGMII:
> +			/* Ensure clock signals and speed is set on all
> +			 * QSGMII links
> +			 */
> +			ocelot_port_writel(ocelot_port,
> +					   DEV_CLOCK_CFG_LINK_SPEED
> +					   (OCELOT_SPEED_1000),
> +					   DEV_CLOCK_CFG);
> +			break;
> +		default:
> +			dev_err(ocelot->dev,
> +				"invalid phy mode for port%d, (Q)SGMII only\n",
> +				port);
> +			of_node_put(portnp);
> +			return -EINVAL;
> +		}
> +
> +		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err == -EPROBE_DEFER)
> +				dev_dbg(ocelot->dev, "deferring probe\n");
> +			else
> +				dev_err(ocelot->dev,
> +					"missing SerDes phys for port%d\n",
> +					port);
> +
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		priv->serdes = serdes;
> +	}
> +
> +	return 0;
> +}
> +
>  static int mscc_ocelot_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> -	struct device_node *ports, *portnp;
>  	int err, irq_xtr, irq_ptp_rdy;
> +	struct device_node *ports;
>  	struct ocelot *ocelot;
>  	struct regmap *hsio;
>  	unsigned int i;
> @@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ports = of_get_child_by_name(np, "ethernet-ports");
>  	if (!ports) {
> -		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
> +		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
>  		return -ENODEV;
>  	}
>  
>  	ocelot->num_phys_ports = of_get_child_count(ports);
>  
> -	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> -				     sizeof(struct ocelot_port *), GFP_KERNEL);
> -	if (!ocelot->ports) {
> -		err = -ENOMEM;
> -		goto out_put_ports;
> -	}
> -
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
> @@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	if (err)
>  		goto out_put_ports;
>  
> +	err = mscc_ocelot_init_ports(pdev, ports);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> @@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	/* No NPI port */
> -	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> -			     OCELOT_TAG_PREFIX_NONE);
> -
> -	for_each_available_child_of_node(ports, portnp) {
> -		struct ocelot_port_private *priv;
> -		struct ocelot_port *ocelot_port;
> -		struct device_node *phy_node;
> -		phy_interface_t phy_mode;
> -		struct phy_device *phy;
> -		struct regmap *target;
> -		struct resource *res;
> -		struct phy *serdes;
> -		char res_name[8];
> -		u32 port;
> -
> -		if (of_property_read_u32(portnp, "reg", &port))
> -			continue;
> -
> -		snprintf(res_name, sizeof(res_name), "port%d", port);
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> -						   res_name);
> -		target = ocelot_regmap_init(ocelot, res);
> -		if (IS_ERR(target))
> -			continue;
> -
> -		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> -		if (!phy_node)
> -			continue;
> -
> -		phy = of_phy_find_device(phy_node);
> -		of_node_put(phy_node);
> -		if (!phy)
> -			continue;
> -
> -		err = ocelot_probe_port(ocelot, port, target, phy);
> -		if (err) {
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		ocelot_port = ocelot->ports[port];
> -		priv = container_of(ocelot_port, struct ocelot_port_private,
> -				    port);
> -
> -		of_get_phy_mode(portnp, &phy_mode);
> -
> -		ocelot_port->phy_mode = phy_mode;
> -
> -		switch (ocelot_port->phy_mode) {
> -		case PHY_INTERFACE_MODE_NA:
> -			continue;
> -		case PHY_INTERFACE_MODE_SGMII:
> -			break;
> -		case PHY_INTERFACE_MODE_QSGMII:
> -			/* Ensure clock signals and speed is set on all
> -			 * QSGMII links
> -			 */
> -			ocelot_port_writel(ocelot_port,
> -					   DEV_CLOCK_CFG_LINK_SPEED
> -					   (OCELOT_SPEED_1000),
> -					   DEV_CLOCK_CFG);
> -			break;
> -		default:
> -			dev_err(ocelot->dev,
> -				"invalid phy mode for port%d, (Q)SGMII only\n",
> -				port);
> -			of_node_put(portnp);
> -			err = -EINVAL;
> -			goto out_put_ports;
> -		}
> -
> -		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> -		if (IS_ERR(serdes)) {
> -			err = PTR_ERR(serdes);
> -			if (err == -EPROBE_DEFER)
> -				dev_dbg(ocelot->dev, "deferring probe\n");
> -			else
> -				dev_err(ocelot->dev,
> -					"missing SerDes phys for port%d\n",
> -					port);
> -
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		priv->serdes = serdes;
> -	}
> -
>  	register_netdevice_notifier(&ocelot_netdevice_nb);
>  	register_switchdev_notifier(&ocelot_switchdev_nb);
>  	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:29+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This driver was not unregistering its network interfaces on unbind.
> Now it is.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> No longer call mscc_ocelot_release_ports from the regular exit path of
> mscc_ocelot_init_ports, which was incorrect.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index ff4a01424953..252c49b5f22b 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> +{
> +	int port;
> +
> +	for (port = 0; port < ocelot->num_phys_ports; port++) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +
> +		ocelot_port = ocelot->ports[port];
> +		if (!ocelot_port)
> +			continue;
> +
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		unregister_netdev(priv->dev);
> +		free_netdev(priv->dev);
> +	}
> +}
> +
>  static int mscc_ocelot_init_ports(struct platform_device *pdev,
>  				  struct device_node *ports)
>  {
> @@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
>  	struct ocelot *ocelot = platform_get_drvdata(pdev);
>  
>  	ocelot_deinit_timestamp(ocelot);
> +	mscc_ocelot_release_ports(ocelot);
>  	ocelot_deinit(ocelot);
>  	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
>  	unregister_switchdev_notifier(&ocelot_switchdev_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:30+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Currently mscc_ocelot_init_ports() will skip initializing a port when it
> doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> NULL. Take this into consideration when tearing down the driver, and add
> a new function ocelot_deinit_port() to the switch library, mirror of
> ocelot_init_port(), which needs to be called by the driver for all ports
> it has initialized.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Changes in v2:
> Patch is new.
> 
>  drivers/net/dsa/ocelot/felix.c             |  3 +++
>  drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
>  include/soc/mscc/ocelot.h                  |  1 +
>  4 files changed, 14 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index f7b43f8d56ed..64939ee14648 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
>  {
>  	struct ocelot *ocelot = ds->priv;
>  	struct felix *felix = ocelot_to_felix(ocelot);
> +	int port;
>  
>  	if (felix->info->mdio_bus_free)
>  		felix->info->mdio_bus_free(ocelot);
>  
> +	for (port = 0; port < ocelot->num_phys_ports; port++)
> +		ocelot_deinit_port(ocelot, port);
>  	ocelot_deinit_timestamp(ocelot);
>  	/* stop workqueue thread */
>  	ocelot_deinit(ocelot);
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 83eb7c325061..8518e1d60da4 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
>  
>  void ocelot_deinit(struct ocelot *ocelot)
>  {
> -	struct ocelot_port *port;
> -	int i;
> -
>  	cancel_delayed_work(&ocelot->stats_work);
>  	destroy_workqueue(ocelot->stats_queue);
>  	mutex_destroy(&ocelot->stats_lock);
> -
> -	for (i = 0; i < ocelot->num_phys_ports; i++) {
> -		port = ocelot->ports[i];
> -		skb_queue_purge(&port->tx_skbs);
> -	}
>  }
>  EXPORT_SYMBOL(ocelot_deinit);
>  
> +void ocelot_deinit_port(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_port *ocelot_port = ocelot->ports[port];
> +
> +	skb_queue_purge(&ocelot_port->tx_skbs);
> +}
> +EXPORT_SYMBOL(ocelot_deinit_port);
> +
>  MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 252c49b5f22b..e02fb8bfab63 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
>  		if (!ocelot_port)
>  			continue;
>  
> +		ocelot_deinit_port(ocelot, port);
> +
>  		priv = container_of(ocelot_port, struct ocelot_port_private,
>  				    port);
>  
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 4521dd602ddc..0ac4e7fba086 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
>  int ocelot_init(struct ocelot *ocelot);
>  void ocelot_deinit(struct ocelot *ocelot);
>  void ocelot_init_port(struct ocelot *ocelot, int port);
> +void ocelot_deinit_port(struct ocelot *ocelot, int port);
>  
>  /* DSA callbacks */
>  void ocelot_port_enable(struct ocelot *ocelot, int port,
> -- 
> 2.25.1
> 


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
                     ` (5 preceding siblings ...)
  2020-09-18 15:25   ` Alexandre Belloni
@ 2020-09-18 15:25   ` Alexandre Belloni
  2020-09-18 15:26   ` Alexandre Belloni
  7 siblings, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:25 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:23+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The TX-timestampable skb is added late to the ocelot_port->tx_skbs. It
> is in a race with the TX timestamp IRQ, which checks that queue trying
> to match the timestamp with the skb by the ts_id. The skb should be
> added to the queue before the IRQ can fire.
> 
> Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> None.
> 
>  drivers/net/ethernet/mscc/ocelot_net.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index 0668d23cdbfa..cacabc23215a 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	u8 grp = 0; /* Send everything on CPU group 0 */
>  	unsigned int i, count, last;
>  	int port = priv->chip_port;
> +	bool do_tstamp;
>  
>  	val = ocelot_read(ocelot, QS_INJ_STATUS);
>  	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
> @@ -344,10 +345,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	info.vid = skb_vlan_tag_get(skb);
>  
>  	/* Check if timestamping is needed */
> +	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);
> +
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
>  			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> +			ocelot_port->ts_id++;
> +		}
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> @@ -380,12 +385,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	dev->stats.tx_packets++;
>  	dev->stats.tx_bytes += skb->len;
>  
> -	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
> -		ocelot_port->ts_id++;
> -		return NETDEV_TX_OK;
> -	}
> +	if (!do_tstamp)
> +		dev_kfree_skb_any(skb);
>  
> -	dev_kfree_skb_any(skb);
>  	return NETDEV_TX_OK;
>  }
>  
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:24+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The ocelot_port->ts_id is used to:
> (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
>     with an skb.
> (b) populate the REW_OP from the injection header of the ongoing skb.
> Only then is ocelot_port->ts_id incremented.
> 
> This is a problem because, at least theoretically, another timestampable
> skb might use the same ocelot_port->ts_id before that is incremented.
> Normally all transmit calls are serialized by the netdev transmit
> spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> called by DSA, which has started declaring the NETIF_F_LLTX feature
> since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> slave ports").  So the logic of using and incrementing the timestamp id
> should be atomic per port.
> 
> The solution is to use the global ocelot_port->ts_id only while
> protected by the associated ocelot_port->ts_id_lock. That's where we
> populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> is called for the actual skb, but for felix, it is called for the skb's
> clone. That is something which will also be changed in the future.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Added an extra explanation about NETIF_F_LLTX in commit message.
> 
>  drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
>  drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
>  include/soc/mscc/ocelot.h              |  1 +
>  net/dsa/tag_ocelot.c                   | 11 +++++++----
>  4 files changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 5abb7d2b0a9e..83eb7c325061 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
>  	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> +		spin_lock(&ocelot_port->ts_id_lock);
> +
>  		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
>  		/* Store timestamp ID in cb[0] of sk_buff */
> -		skb->cb[0] = ocelot_port->ts_id % 4;
> +		skb->cb[0] = ocelot_port->ts_id;
> +		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
>  		skb_queue_tail(&ocelot_port->tx_skbs, skb);
> +
> +		spin_unlock(&ocelot_port->ts_id_lock);
>  		return 0;
>  	}
>  	return -ENODATA;
> @@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
>  	struct ocelot_port *ocelot_port = ocelot->ports[port];
>  
>  	skb_queue_head_init(&ocelot_port->tx_skbs);
> +	spin_lock_init(&ocelot_port->ts_id_lock);
>  
>  	/* Basic L2 initialization */
>  
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index cacabc23215a..8490e42e9e2d 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			info.rew_op |= skb->cb[0] << 3;
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index da369b12005f..4521dd602ddc 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -566,6 +566,7 @@ struct ocelot_port {
>  	u8				ptp_cmd;
>  	struct sk_buff_head		tx_skbs;
>  	u8				ts_id;
> +	spinlock_t			ts_id_lock;
>  
>  	phy_interface_t			phy_mode;
>  
> diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
> index 42f327c06dca..b4fc05cafaa6 100644
> --- a/net/dsa/tag_ocelot.c
> +++ b/net/dsa/tag_ocelot.c
> @@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
>  	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
>  
>  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
> +		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
> +
>  		rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		/* Retrieve timestamp ID populated inside skb->cb[0] of the
> +		 * clone by ocelot_port_add_txtstamp_skb
> +		 */
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			rew_op |= clone->cb[0] << 3;
>  
>  		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
>  	}
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:25+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> words of 60 bytes each.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> None.
> 
>  drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 2d6a5f5758f8..83a1ab9393e9 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
>  	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
>  	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
>  	.vcap			= vsc9953_vcap_props,
> -	.shared_queue_sz	= 128 * 1024,
> +	.shared_queue_sz	= 2048 * 1024,
>  	.num_mact_rows		= 2048,
>  	.num_ports		= 10,
>  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:26+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Do not proceed probing if we couldn't allocate memory for the ports
> array, just error out.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 65408bc994c4..904ea299a5e8 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
>  				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports) {
> +		err = -ENOMEM;
> +		goto out_put_ports;
> +	}
>  
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:27+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> ocelot_init() allocates memory, resets the switch and polls for a status
> register, things which can fail. Stop probing the driver in that case,
> and propagate the error result.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node in the VSC7514 driver.
> 
>  drivers/net/dsa/ocelot/felix.c             | 5 ++++-
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index a1e1d3824110..f7b43f8d56ed 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
>  	if (err)
>  		return err;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		return err;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 904ea299a5e8..a1cbb20a7757 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:28+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> mscc_ocelot_probe() is already pretty large and hard to follow. So move
> the code for parsing ports in a separate function.
> 
> This makes it easier for the next patch to just call
> mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Keep a reference to the 'ports' OF node at caller side, in
> mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
> early. The ocelot_init() function depends on it being set correctly.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
>  1 file changed, 110 insertions(+), 99 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index a1cbb20a7757..ff4a01424953 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static int mscc_ocelot_init_ports(struct platform_device *pdev,
> +				  struct device_node *ports)
> +{
> +	struct ocelot *ocelot = platform_get_drvdata(pdev);
> +	struct device_node *portnp;
> +	int err;
> +
> +	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
> +				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports)
> +		return -ENOMEM;
> +
> +	/* No NPI port */
> +	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> +			     OCELOT_TAG_PREFIX_NONE);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +		struct device_node *phy_node;
> +		phy_interface_t phy_mode;
> +		struct phy_device *phy;
> +		struct regmap *target;
> +		struct resource *res;
> +		struct phy *serdes;
> +		char res_name[8];
> +		u32 port;
> +
> +		if (of_property_read_u32(portnp, "reg", &port))
> +			continue;
> +
> +		snprintf(res_name, sizeof(res_name), "port%d", port);
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						   res_name);
> +		target = ocelot_regmap_init(ocelot, res);
> +		if (IS_ERR(target))
> +			continue;
> +
> +		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> +		if (!phy_node)
> +			continue;
> +
> +		phy = of_phy_find_device(phy_node);
> +		of_node_put(phy_node);
> +		if (!phy)
> +			continue;
> +
> +		err = ocelot_probe_port(ocelot, port, target, phy);
> +		if (err) {
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		ocelot_port = ocelot->ports[port];
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		of_get_phy_mode(portnp, &phy_mode);
> +
> +		ocelot_port->phy_mode = phy_mode;
> +
> +		switch (ocelot_port->phy_mode) {
> +		case PHY_INTERFACE_MODE_NA:
> +			continue;
> +		case PHY_INTERFACE_MODE_SGMII:
> +			break;
> +		case PHY_INTERFACE_MODE_QSGMII:
> +			/* Ensure clock signals and speed is set on all
> +			 * QSGMII links
> +			 */
> +			ocelot_port_writel(ocelot_port,
> +					   DEV_CLOCK_CFG_LINK_SPEED
> +					   (OCELOT_SPEED_1000),
> +					   DEV_CLOCK_CFG);
> +			break;
> +		default:
> +			dev_err(ocelot->dev,
> +				"invalid phy mode for port%d, (Q)SGMII only\n",
> +				port);
> +			of_node_put(portnp);
> +			return -EINVAL;
> +		}
> +
> +		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err == -EPROBE_DEFER)
> +				dev_dbg(ocelot->dev, "deferring probe\n");
> +			else
> +				dev_err(ocelot->dev,
> +					"missing SerDes phys for port%d\n",
> +					port);
> +
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		priv->serdes = serdes;
> +	}
> +
> +	return 0;
> +}
> +
>  static int mscc_ocelot_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> -	struct device_node *ports, *portnp;
>  	int err, irq_xtr, irq_ptp_rdy;
> +	struct device_node *ports;
>  	struct ocelot *ocelot;
>  	struct regmap *hsio;
>  	unsigned int i;
> @@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ports = of_get_child_by_name(np, "ethernet-ports");
>  	if (!ports) {
> -		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
> +		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
>  		return -ENODEV;
>  	}
>  
>  	ocelot->num_phys_ports = of_get_child_count(ports);
>  
> -	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> -				     sizeof(struct ocelot_port *), GFP_KERNEL);
> -	if (!ocelot->ports) {
> -		err = -ENOMEM;
> -		goto out_put_ports;
> -	}
> -
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
> @@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	if (err)
>  		goto out_put_ports;
>  
> +	err = mscc_ocelot_init_ports(pdev, ports);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> @@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	/* No NPI port */
> -	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> -			     OCELOT_TAG_PREFIX_NONE);
> -
> -	for_each_available_child_of_node(ports, portnp) {
> -		struct ocelot_port_private *priv;
> -		struct ocelot_port *ocelot_port;
> -		struct device_node *phy_node;
> -		phy_interface_t phy_mode;
> -		struct phy_device *phy;
> -		struct regmap *target;
> -		struct resource *res;
> -		struct phy *serdes;
> -		char res_name[8];
> -		u32 port;
> -
> -		if (of_property_read_u32(portnp, "reg", &port))
> -			continue;
> -
> -		snprintf(res_name, sizeof(res_name), "port%d", port);
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> -						   res_name);
> -		target = ocelot_regmap_init(ocelot, res);
> -		if (IS_ERR(target))
> -			continue;
> -
> -		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> -		if (!phy_node)
> -			continue;
> -
> -		phy = of_phy_find_device(phy_node);
> -		of_node_put(phy_node);
> -		if (!phy)
> -			continue;
> -
> -		err = ocelot_probe_port(ocelot, port, target, phy);
> -		if (err) {
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		ocelot_port = ocelot->ports[port];
> -		priv = container_of(ocelot_port, struct ocelot_port_private,
> -				    port);
> -
> -		of_get_phy_mode(portnp, &phy_mode);
> -
> -		ocelot_port->phy_mode = phy_mode;
> -
> -		switch (ocelot_port->phy_mode) {
> -		case PHY_INTERFACE_MODE_NA:
> -			continue;
> -		case PHY_INTERFACE_MODE_SGMII:
> -			break;
> -		case PHY_INTERFACE_MODE_QSGMII:
> -			/* Ensure clock signals and speed is set on all
> -			 * QSGMII links
> -			 */
> -			ocelot_port_writel(ocelot_port,
> -					   DEV_CLOCK_CFG_LINK_SPEED
> -					   (OCELOT_SPEED_1000),
> -					   DEV_CLOCK_CFG);
> -			break;
> -		default:
> -			dev_err(ocelot->dev,
> -				"invalid phy mode for port%d, (Q)SGMII only\n",
> -				port);
> -			of_node_put(portnp);
> -			err = -EINVAL;
> -			goto out_put_ports;
> -		}
> -
> -		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> -		if (IS_ERR(serdes)) {
> -			err = PTR_ERR(serdes);
> -			if (err == -EPROBE_DEFER)
> -				dev_dbg(ocelot->dev, "deferring probe\n");
> -			else
> -				dev_err(ocelot->dev,
> -					"missing SerDes phys for port%d\n",
> -					port);
> -
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		priv->serdes = serdes;
> -	}
> -
>  	register_netdevice_notifier(&ocelot_netdevice_nb);
>  	register_switchdev_notifier(&ocelot_switchdev_nb);
>  	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:29+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This driver was not unregistering its network interfaces on unbind.
> Now it is.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> No longer call mscc_ocelot_release_ports from the regular exit path of
> mscc_ocelot_init_ports, which was incorrect.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index ff4a01424953..252c49b5f22b 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> +{
> +	int port;
> +
> +	for (port = 0; port < ocelot->num_phys_ports; port++) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +
> +		ocelot_port = ocelot->ports[port];
> +		if (!ocelot_port)
> +			continue;
> +
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		unregister_netdev(priv->dev);
> +		free_netdev(priv->dev);
> +	}
> +}
> +
>  static int mscc_ocelot_init_ports(struct platform_device *pdev,
>  				  struct device_node *ports)
>  {
> @@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
>  	struct ocelot *ocelot = platform_get_drvdata(pdev);
>  
>  	ocelot_deinit_timestamp(ocelot);
> +	mscc_ocelot_release_ports(ocelot);
>  	ocelot_deinit(ocelot);
>  	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
>  	unregister_switchdev_notifier(&ocelot_switchdev_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:30+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Currently mscc_ocelot_init_ports() will skip initializing a port when it
> doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> NULL. Take this into consideration when tearing down the driver, and add
> a new function ocelot_deinit_port() to the switch library, mirror of
> ocelot_init_port(), which needs to be called by the driver for all ports
> it has initialized.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Changes in v2:
> Patch is new.
> 
>  drivers/net/dsa/ocelot/felix.c             |  3 +++
>  drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
>  include/soc/mscc/ocelot.h                  |  1 +
>  4 files changed, 14 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index f7b43f8d56ed..64939ee14648 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
>  {
>  	struct ocelot *ocelot = ds->priv;
>  	struct felix *felix = ocelot_to_felix(ocelot);
> +	int port;
>  
>  	if (felix->info->mdio_bus_free)
>  		felix->info->mdio_bus_free(ocelot);
>  
> +	for (port = 0; port < ocelot->num_phys_ports; port++)
> +		ocelot_deinit_port(ocelot, port);
>  	ocelot_deinit_timestamp(ocelot);
>  	/* stop workqueue thread */
>  	ocelot_deinit(ocelot);
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 83eb7c325061..8518e1d60da4 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
>  
>  void ocelot_deinit(struct ocelot *ocelot)
>  {
> -	struct ocelot_port *port;
> -	int i;
> -
>  	cancel_delayed_work(&ocelot->stats_work);
>  	destroy_workqueue(ocelot->stats_queue);
>  	mutex_destroy(&ocelot->stats_lock);
> -
> -	for (i = 0; i < ocelot->num_phys_ports; i++) {
> -		port = ocelot->ports[i];
> -		skb_queue_purge(&port->tx_skbs);
> -	}
>  }
>  EXPORT_SYMBOL(ocelot_deinit);
>  
> +void ocelot_deinit_port(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_port *ocelot_port = ocelot->ports[port];
> +
> +	skb_queue_purge(&ocelot_port->tx_skbs);
> +}
> +EXPORT_SYMBOL(ocelot_deinit_port);
> +
>  MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 252c49b5f22b..e02fb8bfab63 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
>  		if (!ocelot_port)
>  			continue;
>  
> +		ocelot_deinit_port(ocelot, port);
> +
>  		priv = container_of(ocelot_port, struct ocelot_port_private,
>  				    port);
>  
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 4521dd602ddc..0ac4e7fba086 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
>  int ocelot_init(struct ocelot *ocelot);
>  void ocelot_deinit(struct ocelot *ocelot);
>  void ocelot_init_port(struct ocelot *ocelot, int port);
> +void ocelot_deinit_port(struct ocelot *ocelot, int port);
>  
>  /* DSA callbacks */
>  void ocelot_port_enable(struct ocelot *ocelot, int port,
> -- 
> 2.25.1
> 


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping
  2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
                     ` (6 preceding siblings ...)
  2020-09-18 15:25   ` Alexandre Belloni
@ 2020-09-18 15:26   ` Alexandre Belloni
  2020-09-18 15:27     ` Alexandre Belloni
  7 siblings, 1 reply; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:26 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:23+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The TX-timestampable skb is added late to the ocelot_port->tx_skbs. It
> is in a race with the TX timestamp IRQ, which checks that queue trying
> to match the timestamp with the skb by the ts_id. The skb should be
> added to the queue before the IRQ can fire.
> 
> Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> None.
> 
>  drivers/net/ethernet/mscc/ocelot_net.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index 0668d23cdbfa..cacabc23215a 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	u8 grp = 0; /* Send everything on CPU group 0 */
>  	unsigned int i, count, last;
>  	int port = priv->chip_port;
> +	bool do_tstamp;
>  
>  	val = ocelot_read(ocelot, QS_INJ_STATUS);
>  	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
> @@ -344,10 +345,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	info.vid = skb_vlan_tag_get(skb);
>  
>  	/* Check if timestamping is needed */
> +	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);
> +
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
>  			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> +			ocelot_port->ts_id++;
> +		}
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> @@ -380,12 +385,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  	dev->stats.tx_packets++;
>  	dev->stats.tx_bytes += skb->len;
>  
> -	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
> -		ocelot_port->ts_id++;
> -		return NETDEV_TX_OK;
> -	}
> +	if (!do_tstamp)
> +		dev_kfree_skb_any(skb);
>  
> -	dev_kfree_skb_any(skb);
>  	return NETDEV_TX_OK;
>  }
>  
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:24+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The ocelot_port->ts_id is used to:
> (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
>     with an skb.
> (b) populate the REW_OP from the injection header of the ongoing skb.
> Only then is ocelot_port->ts_id incremented.
> 
> This is a problem because, at least theoretically, another timestampable
> skb might use the same ocelot_port->ts_id before that is incremented.
> Normally all transmit calls are serialized by the netdev transmit
> spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> called by DSA, which has started declaring the NETIF_F_LLTX feature
> since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> slave ports").  So the logic of using and incrementing the timestamp id
> should be atomic per port.
> 
> The solution is to use the global ocelot_port->ts_id only while
> protected by the associated ocelot_port->ts_id_lock. That's where we
> populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> is called for the actual skb, but for felix, it is called for the skb's
> clone. That is something which will also be changed in the future.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Added an extra explanation about NETIF_F_LLTX in commit message.
> 
>  drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
>  drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
>  include/soc/mscc/ocelot.h              |  1 +
>  net/dsa/tag_ocelot.c                   | 11 +++++++----
>  4 files changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 5abb7d2b0a9e..83eb7c325061 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
>  	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> +		spin_lock(&ocelot_port->ts_id_lock);
> +
>  		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
>  		/* Store timestamp ID in cb[0] of sk_buff */
> -		skb->cb[0] = ocelot_port->ts_id % 4;
> +		skb->cb[0] = ocelot_port->ts_id;
> +		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
>  		skb_queue_tail(&ocelot_port->tx_skbs, skb);
> +
> +		spin_unlock(&ocelot_port->ts_id_lock);
>  		return 0;
>  	}
>  	return -ENODATA;
> @@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
>  	struct ocelot_port *ocelot_port = ocelot->ports[port];
>  
>  	skb_queue_head_init(&ocelot_port->tx_skbs);
> +	spin_lock_init(&ocelot_port->ts_id_lock);
>  
>  	/* Basic L2 initialization */
>  
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index cacabc23215a..8490e42e9e2d 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			info.rew_op |= skb->cb[0] << 3;
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index da369b12005f..4521dd602ddc 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -566,6 +566,7 @@ struct ocelot_port {
>  	u8				ptp_cmd;
>  	struct sk_buff_head		tx_skbs;
>  	u8				ts_id;
> +	spinlock_t			ts_id_lock;
>  
>  	phy_interface_t			phy_mode;
>  
> diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
> index 42f327c06dca..b4fc05cafaa6 100644
> --- a/net/dsa/tag_ocelot.c
> +++ b/net/dsa/tag_ocelot.c
> @@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
>  	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
>  
>  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
> +		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
> +
>  		rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		/* Retrieve timestamp ID populated inside skb->cb[0] of the
> +		 * clone by ocelot_port_add_txtstamp_skb
> +		 */
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			rew_op |= clone->cb[0] << 3;
>  
>  		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
>  	}
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:25+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> words of 60 bytes each.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> None.
> 
>  drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 2d6a5f5758f8..83a1ab9393e9 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
>  	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
>  	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
>  	.vcap			= vsc9953_vcap_props,
> -	.shared_queue_sz	= 128 * 1024,
> +	.shared_queue_sz	= 2048 * 1024,
>  	.num_mact_rows		= 2048,
>  	.num_ports		= 10,
>  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:26+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Do not proceed probing if we couldn't allocate memory for the ports
> array, just error out.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 65408bc994c4..904ea299a5e8 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
>  				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports) {
> +		err = -ENOMEM;
> +		goto out_put_ports;
> +	}
>  
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:27+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> ocelot_init() allocates memory, resets the switch and polls for a status
> register, things which can fail. Stop probing the driver in that case,
> and propagate the error result.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node in the VSC7514 driver.
> 
>  drivers/net/dsa/ocelot/felix.c             | 5 ++++-
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index a1e1d3824110..f7b43f8d56ed 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
>  	if (err)
>  		return err;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		return err;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 904ea299a5e8..a1cbb20a7757 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:28+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> mscc_ocelot_probe() is already pretty large and hard to follow. So move
> the code for parsing ports in a separate function.
> 
> This makes it easier for the next patch to just call
> mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> Keep a reference to the 'ports' OF node at caller side, in
> mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
> early. The ocelot_init() function depends on it being set correctly.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
>  1 file changed, 110 insertions(+), 99 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index a1cbb20a7757..ff4a01424953 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static int mscc_ocelot_init_ports(struct platform_device *pdev,
> +				  struct device_node *ports)
> +{
> +	struct ocelot *ocelot = platform_get_drvdata(pdev);
> +	struct device_node *portnp;
> +	int err;
> +
> +	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
> +				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports)
> +		return -ENOMEM;
> +
> +	/* No NPI port */
> +	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> +			     OCELOT_TAG_PREFIX_NONE);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +		struct device_node *phy_node;
> +		phy_interface_t phy_mode;
> +		struct phy_device *phy;
> +		struct regmap *target;
> +		struct resource *res;
> +		struct phy *serdes;
> +		char res_name[8];
> +		u32 port;
> +
> +		if (of_property_read_u32(portnp, "reg", &port))
> +			continue;
> +
> +		snprintf(res_name, sizeof(res_name), "port%d", port);
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						   res_name);
> +		target = ocelot_regmap_init(ocelot, res);
> +		if (IS_ERR(target))
> +			continue;
> +
> +		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> +		if (!phy_node)
> +			continue;
> +
> +		phy = of_phy_find_device(phy_node);
> +		of_node_put(phy_node);
> +		if (!phy)
> +			continue;
> +
> +		err = ocelot_probe_port(ocelot, port, target, phy);
> +		if (err) {
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		ocelot_port = ocelot->ports[port];
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		of_get_phy_mode(portnp, &phy_mode);
> +
> +		ocelot_port->phy_mode = phy_mode;
> +
> +		switch (ocelot_port->phy_mode) {
> +		case PHY_INTERFACE_MODE_NA:
> +			continue;
> +		case PHY_INTERFACE_MODE_SGMII:
> +			break;
> +		case PHY_INTERFACE_MODE_QSGMII:
> +			/* Ensure clock signals and speed is set on all
> +			 * QSGMII links
> +			 */
> +			ocelot_port_writel(ocelot_port,
> +					   DEV_CLOCK_CFG_LINK_SPEED
> +					   (OCELOT_SPEED_1000),
> +					   DEV_CLOCK_CFG);
> +			break;
> +		default:
> +			dev_err(ocelot->dev,
> +				"invalid phy mode for port%d, (Q)SGMII only\n",
> +				port);
> +			of_node_put(portnp);
> +			return -EINVAL;
> +		}
> +
> +		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err == -EPROBE_DEFER)
> +				dev_dbg(ocelot->dev, "deferring probe\n");
> +			else
> +				dev_err(ocelot->dev,
> +					"missing SerDes phys for port%d\n",
> +					port);
> +
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		priv->serdes = serdes;
> +	}
> +
> +	return 0;
> +}
> +
>  static int mscc_ocelot_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> -	struct device_node *ports, *portnp;
>  	int err, irq_xtr, irq_ptp_rdy;
> +	struct device_node *ports;
>  	struct ocelot *ocelot;
>  	struct regmap *hsio;
>  	unsigned int i;
> @@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ports = of_get_child_by_name(np, "ethernet-ports");
>  	if (!ports) {
> -		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
> +		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
>  		return -ENODEV;
>  	}
>  
>  	ocelot->num_phys_ports = of_get_child_count(ports);
>  
> -	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> -				     sizeof(struct ocelot_port *), GFP_KERNEL);
> -	if (!ocelot->ports) {
> -		err = -ENOMEM;
> -		goto out_put_ports;
> -	}
> -
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
> @@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	if (err)
>  		goto out_put_ports;
>  
> +	err = mscc_ocelot_init_ports(pdev, ports);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> @@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	/* No NPI port */
> -	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> -			     OCELOT_TAG_PREFIX_NONE);
> -
> -	for_each_available_child_of_node(ports, portnp) {
> -		struct ocelot_port_private *priv;
> -		struct ocelot_port *ocelot_port;
> -		struct device_node *phy_node;
> -		phy_interface_t phy_mode;
> -		struct phy_device *phy;
> -		struct regmap *target;
> -		struct resource *res;
> -		struct phy *serdes;
> -		char res_name[8];
> -		u32 port;
> -
> -		if (of_property_read_u32(portnp, "reg", &port))
> -			continue;
> -
> -		snprintf(res_name, sizeof(res_name), "port%d", port);
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> -						   res_name);
> -		target = ocelot_regmap_init(ocelot, res);
> -		if (IS_ERR(target))
> -			continue;
> -
> -		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> -		if (!phy_node)
> -			continue;
> -
> -		phy = of_phy_find_device(phy_node);
> -		of_node_put(phy_node);
> -		if (!phy)
> -			continue;
> -
> -		err = ocelot_probe_port(ocelot, port, target, phy);
> -		if (err) {
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		ocelot_port = ocelot->ports[port];
> -		priv = container_of(ocelot_port, struct ocelot_port_private,
> -				    port);
> -
> -		of_get_phy_mode(portnp, &phy_mode);
> -
> -		ocelot_port->phy_mode = phy_mode;
> -
> -		switch (ocelot_port->phy_mode) {
> -		case PHY_INTERFACE_MODE_NA:
> -			continue;
> -		case PHY_INTERFACE_MODE_SGMII:
> -			break;
> -		case PHY_INTERFACE_MODE_QSGMII:
> -			/* Ensure clock signals and speed is set on all
> -			 * QSGMII links
> -			 */
> -			ocelot_port_writel(ocelot_port,
> -					   DEV_CLOCK_CFG_LINK_SPEED
> -					   (OCELOT_SPEED_1000),
> -					   DEV_CLOCK_CFG);
> -			break;
> -		default:
> -			dev_err(ocelot->dev,
> -				"invalid phy mode for port%d, (Q)SGMII only\n",
> -				port);
> -			of_node_put(portnp);
> -			err = -EINVAL;
> -			goto out_put_ports;
> -		}
> -
> -		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> -		if (IS_ERR(serdes)) {
> -			err = PTR_ERR(serdes);
> -			if (err == -EPROBE_DEFER)
> -				dev_dbg(ocelot->dev, "deferring probe\n");
> -			else
> -				dev_err(ocelot->dev,
> -					"missing SerDes phys for port%d\n",
> -					port);
> -
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		priv->serdes = serdes;
> -	}
> -
>  	register_netdevice_notifier(&ocelot_netdevice_nb);
>  	register_switchdev_notifier(&ocelot_switchdev_nb);
>  	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:29+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This driver was not unregistering its network interfaces on unbind.
> Now it is.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
> Changes in v2:
> No longer call mscc_ocelot_release_ports from the regular exit path of
> mscc_ocelot_init_ports, which was incorrect.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index ff4a01424953..252c49b5f22b 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> +{
> +	int port;
> +
> +	for (port = 0; port < ocelot->num_phys_ports; port++) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +
> +		ocelot_port = ocelot->ports[port];
> +		if (!ocelot_port)
> +			continue;
> +
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		unregister_netdev(priv->dev);
> +		free_netdev(priv->dev);
> +	}
> +}
> +
>  static int mscc_ocelot_init_ports(struct platform_device *pdev,
>  				  struct device_node *ports)
>  {
> @@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
>  	struct ocelot *ocelot = platform_get_drvdata(pdev);
>  
>  	ocelot_deinit_timestamp(ocelot);
> +	mscc_ocelot_release_ports(ocelot);
>  	ocelot_deinit(ocelot);
>  	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
>  	unregister_switchdev_notifier(&ocelot_switchdev_nb);
> -- 
> 2.25.1
> 

On 18/09/2020 04:07:30+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Currently mscc_ocelot_init_ports() will skip initializing a port when it
> doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> NULL. Take this into consideration when tearing down the driver, and add
> a new function ocelot_deinit_port() to the switch library, mirror of
> ocelot_init_port(), which needs to be called by the driver for all ports
> it has initialized.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Changes in v2:
> Patch is new.
> 
>  drivers/net/dsa/ocelot/felix.c             |  3 +++
>  drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
>  include/soc/mscc/ocelot.h                  |  1 +
>  4 files changed, 14 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index f7b43f8d56ed..64939ee14648 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
>  {
>  	struct ocelot *ocelot = ds->priv;
>  	struct felix *felix = ocelot_to_felix(ocelot);
> +	int port;
>  
>  	if (felix->info->mdio_bus_free)
>  		felix->info->mdio_bus_free(ocelot);
>  
> +	for (port = 0; port < ocelot->num_phys_ports; port++)
> +		ocelot_deinit_port(ocelot, port);
>  	ocelot_deinit_timestamp(ocelot);
>  	/* stop workqueue thread */
>  	ocelot_deinit(ocelot);
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 83eb7c325061..8518e1d60da4 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
>  
>  void ocelot_deinit(struct ocelot *ocelot)
>  {
> -	struct ocelot_port *port;
> -	int i;
> -
>  	cancel_delayed_work(&ocelot->stats_work);
>  	destroy_workqueue(ocelot->stats_queue);
>  	mutex_destroy(&ocelot->stats_lock);
> -
> -	for (i = 0; i < ocelot->num_phys_ports; i++) {
> -		port = ocelot->ports[i];
> -		skb_queue_purge(&port->tx_skbs);
> -	}
>  }
>  EXPORT_SYMBOL(ocelot_deinit);
>  
> +void ocelot_deinit_port(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_port *ocelot_port = ocelot->ports[port];
> +
> +	skb_queue_purge(&ocelot_port->tx_skbs);
> +}
> +EXPORT_SYMBOL(ocelot_deinit_port);
> +
>  MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 252c49b5f22b..e02fb8bfab63 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
>  		if (!ocelot_port)
>  			continue;
>  
> +		ocelot_deinit_port(ocelot, port);
> +
>  		priv = container_of(ocelot_port, struct ocelot_port_private,
>  				    port);
>  
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 4521dd602ddc..0ac4e7fba086 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
>  int ocelot_init(struct ocelot *ocelot);
>  void ocelot_deinit(struct ocelot *ocelot);
>  void ocelot_init_port(struct ocelot *ocelot, int port);
> +void ocelot_deinit_port(struct ocelot *ocelot, int port);
>  
>  /* DSA callbacks */
>  void ocelot_port_enable(struct ocelot *ocelot, int port,
> -- 
> 2.25.1
> 


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping
  2020-09-18 15:26   ` Alexandre Belloni
@ 2020-09-18 15:27     ` Alexandre Belloni
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:27 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 17:26:03+0200, Alexandre Belloni wrote:
> On 18/09/2020 04:07:23+0300, Vladimir Oltean wrote:
> > From: Vladimir Oltean <vladimir.oltean@nxp.com>
> > 
> > The TX-timestampable skb is added late to the ocelot_port->tx_skbs. It
> > is in a race with the TX timestamp IRQ, which checks that queue trying
> > to match the timestamp with the skb by the ts_id. The skb should be
> > added to the queue before the IRQ can fire.
> > 
> > Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
> > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> > Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> 
> Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
> Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

Ok, this misfired, sorry about the noise.

> 
> > ---
> > Changes in v2:
> > None.
> > 
> >  drivers/net/ethernet/mscc/ocelot_net.c | 14 ++++++++------
> >  1 file changed, 8 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> > index 0668d23cdbfa..cacabc23215a 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_net.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> > @@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
> >  	u8 grp = 0; /* Send everything on CPU group 0 */
> >  	unsigned int i, count, last;
> >  	int port = priv->chip_port;
> > +	bool do_tstamp;
> >  
> >  	val = ocelot_read(ocelot, QS_INJ_STATUS);
> >  	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
> > @@ -344,10 +345,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
> >  	info.vid = skb_vlan_tag_get(skb);
> >  
> >  	/* Check if timestamping is needed */
> > +	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);
> > +
> >  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
> >  		info.rew_op = ocelot_port->ptp_cmd;
> > -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> > +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> >  			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> > +			ocelot_port->ts_id++;
> > +		}
> >  	}
> >  
> >  	ocelot_gen_ifh(ifh, &info);
> > @@ -380,12 +385,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
> >  	dev->stats.tx_packets++;
> >  	dev->stats.tx_bytes += skb->len;
> >  
> > -	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
> > -		ocelot_port->ts_id++;
> > -		return NETDEV_TX_OK;
> > -	}
> > +	if (!do_tstamp)
> > +		dev_kfree_skb_any(skb);
> >  
> > -	dev_kfree_skb_any(skb);
> >  	return NETDEV_TX_OK;
> >  }
> >  
> > -- 
> > 2.25.1
> > 
> 
> On 18/09/2020 04:07:24+0300, Vladimir Oltean wrote:
> > From: Vladimir Oltean <vladimir.oltean@nxp.com>
> > 
> > The ocelot_port->ts_id is used to:
> > (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
> >     with an skb.
> > (b) populate the REW_OP from the injection header of the ongoing skb.
> > Only then is ocelot_port->ts_id incremented.
> > 
> > This is a problem because, at least theoretically, another timestampable
> > skb might use the same ocelot_port->ts_id before that is incremented.
> > Normally all transmit calls are serialized by the netdev transmit
> > spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> > called by DSA, which has started declaring the NETIF_F_LLTX feature
> > since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> > slave ports").  So the logic of using and incrementing the timestamp id
> > should be atomic per port.
> > 
> > The solution is to use the global ocelot_port->ts_id only while
> > protected by the associated ocelot_port->ts_id_lock. That's where we
> > populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> > is called for the actual skb, but for felix, it is called for the skb's
> > clone. That is something which will also be changed in the future.
> > 
> > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> > Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> > ---
> > Changes in v2:
> > Added an extra explanation about NETIF_F_LLTX in commit message.
> > 
> >  drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
> >  drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
> >  include/soc/mscc/ocelot.h              |  1 +
> >  net/dsa/tag_ocelot.c                   | 11 +++++++----
> >  4 files changed, 17 insertions(+), 9 deletions(-)
> > 
> > diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> > index 5abb7d2b0a9e..83eb7c325061 100644
> > --- a/drivers/net/ethernet/mscc/ocelot.c
> > +++ b/drivers/net/ethernet/mscc/ocelot.c
> > @@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
> >  
> >  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
> >  	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> > +		spin_lock(&ocelot_port->ts_id_lock);
> > +
> >  		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
> >  		/* Store timestamp ID in cb[0] of sk_buff */
> > -		skb->cb[0] = ocelot_port->ts_id % 4;
> > +		skb->cb[0] = ocelot_port->ts_id;
> > +		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
> >  		skb_queue_tail(&ocelot_port->tx_skbs, skb);
> > +
> > +		spin_unlock(&ocelot_port->ts_id_lock);
> >  		return 0;
> >  	}
> >  	return -ENODATA;
> > @@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
> >  	struct ocelot_port *ocelot_port = ocelot->ports[port];
> >  
> >  	skb_queue_head_init(&ocelot_port->tx_skbs);
> > +	spin_lock_init(&ocelot_port->ts_id_lock);
> >  
> >  	/* Basic L2 initialization */
> >  
> > diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> > index cacabc23215a..8490e42e9e2d 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_net.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> > @@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
> >  
> >  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
> >  		info.rew_op = ocelot_port->ptp_cmd;
> > -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> > -			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> > -			ocelot_port->ts_id++;
> > -		}
> > +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> > +			info.rew_op |= skb->cb[0] << 3;
> >  	}
> >  
> >  	ocelot_gen_ifh(ifh, &info);
> > diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> > index da369b12005f..4521dd602ddc 100644
> > --- a/include/soc/mscc/ocelot.h
> > +++ b/include/soc/mscc/ocelot.h
> > @@ -566,6 +566,7 @@ struct ocelot_port {
> >  	u8				ptp_cmd;
> >  	struct sk_buff_head		tx_skbs;
> >  	u8				ts_id;
> > +	spinlock_t			ts_id_lock;
> >  
> >  	phy_interface_t			phy_mode;
> >  
> > diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
> > index 42f327c06dca..b4fc05cafaa6 100644
> > --- a/net/dsa/tag_ocelot.c
> > +++ b/net/dsa/tag_ocelot.c
> > @@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
> >  	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
> >  
> >  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
> > +		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
> > +
> >  		rew_op = ocelot_port->ptp_cmd;
> > -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> > -			rew_op |= (ocelot_port->ts_id  % 4) << 3;
> > -			ocelot_port->ts_id++;
> > -		}
> > +		/* Retrieve timestamp ID populated inside skb->cb[0] of the
> > +		 * clone by ocelot_port_add_txtstamp_skb
> > +		 */
> > +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> > +			rew_op |= clone->cb[0] << 3;
> >  
> >  		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
> >  	}
> > -- 
> > 2.25.1
> > 
> 
> On 18/09/2020 04:07:25+0300, Vladimir Oltean wrote:
> > From: Vladimir Oltean <vladimir.oltean@nxp.com>
> > 
> > The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> > words of 60 bytes each.
> > 
> > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> > Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> > ---
> > Changes in v2:
> > None.
> > 
> >  drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> > index 2d6a5f5758f8..83a1ab9393e9 100644
> > --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> > +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> > @@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
> >  	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
> >  	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
> >  	.vcap			= vsc9953_vcap_props,
> > -	.shared_queue_sz	= 128 * 1024,
> > +	.shared_queue_sz	= 2048 * 1024,
> >  	.num_mact_rows		= 2048,
> >  	.num_ports		= 10,
> >  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
> > -- 
> > 2.25.1
> > 
> 
> On 18/09/2020 04:07:26+0300, Vladimir Oltean wrote:
> > From: Vladimir Oltean <vladimir.oltean@nxp.com>
> > 
> > Do not proceed probing if we couldn't allocate memory for the ports
> > array, just error out.
> > 
> > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> > Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> > ---
> > Changes in v2:
> > Stopped leaking the 'ports' OF node.
> > 
> >  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
> >  1 file changed, 4 insertions(+)
> > 
> > diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > index 65408bc994c4..904ea299a5e8 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > @@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
> >  
> >  	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> >  				     sizeof(struct ocelot_port *), GFP_KERNEL);
> > +	if (!ocelot->ports) {
> > +		err = -ENOMEM;
> > +		goto out_put_ports;
> > +	}
> >  
> >  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
> >  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> > -- 
> > 2.25.1
> > 
> 
> On 18/09/2020 04:07:27+0300, Vladimir Oltean wrote:
> > From: Vladimir Oltean <vladimir.oltean@nxp.com>
> > 
> > ocelot_init() allocates memory, resets the switch and polls for a status
> > register, things which can fail. Stop probing the driver in that case,
> > and propagate the error result.
> > 
> > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> > Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> > ---
> > Changes in v2:
> > Stopped leaking the 'ports' OF node in the VSC7514 driver.
> > 
> >  drivers/net/dsa/ocelot/felix.c             | 5 ++++-
> >  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
> >  2 files changed, 8 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> > index a1e1d3824110..f7b43f8d56ed 100644
> > --- a/drivers/net/dsa/ocelot/felix.c
> > +++ b/drivers/net/dsa/ocelot/felix.c
> > @@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
> >  	if (err)
> >  		return err;
> >  
> > -	ocelot_init(ocelot);
> > +	err = ocelot_init(ocelot);
> > +	if (err)
> > +		return err;
> > +
> >  	if (ocelot->ptp) {
> >  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
> >  		if (err) {
> > diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > index 904ea299a5e8..a1cbb20a7757 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > @@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
> >  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> >  	ocelot->vcap = vsc7514_vcap_props;
> >  
> > -	ocelot_init(ocelot);
> > +	err = ocelot_init(ocelot);
> > +	if (err)
> > +		goto out_put_ports;
> > +
> >  	if (ocelot->ptp) {
> >  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
> >  		if (err) {
> > -- 
> > 2.25.1
> > 
> 
> On 18/09/2020 04:07:28+0300, Vladimir Oltean wrote:
> > From: Vladimir Oltean <vladimir.oltean@nxp.com>
> > 
> > mscc_ocelot_probe() is already pretty large and hard to follow. So move
> > the code for parsing ports in a separate function.
> > 
> > This makes it easier for the next patch to just call
> > mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> > 
> > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> > Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> > ---
> > Changes in v2:
> > Keep a reference to the 'ports' OF node at caller side, in
> > mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
> > early. The ocelot_init() function depends on it being set correctly.
> > 
> >  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
> >  1 file changed, 110 insertions(+), 99 deletions(-)
> > 
> > diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > index a1cbb20a7757..ff4a01424953 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > @@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
> >  	.enable		= ocelot_ptp_enable,
> >  };
> >  
> > +static int mscc_ocelot_init_ports(struct platform_device *pdev,
> > +				  struct device_node *ports)
> > +{
> > +	struct ocelot *ocelot = platform_get_drvdata(pdev);
> > +	struct device_node *portnp;
> > +	int err;
> > +
> > +	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
> > +				     sizeof(struct ocelot_port *), GFP_KERNEL);
> > +	if (!ocelot->ports)
> > +		return -ENOMEM;
> > +
> > +	/* No NPI port */
> > +	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> > +			     OCELOT_TAG_PREFIX_NONE);
> > +
> > +	for_each_available_child_of_node(ports, portnp) {
> > +		struct ocelot_port_private *priv;
> > +		struct ocelot_port *ocelot_port;
> > +		struct device_node *phy_node;
> > +		phy_interface_t phy_mode;
> > +		struct phy_device *phy;
> > +		struct regmap *target;
> > +		struct resource *res;
> > +		struct phy *serdes;
> > +		char res_name[8];
> > +		u32 port;
> > +
> > +		if (of_property_read_u32(portnp, "reg", &port))
> > +			continue;
> > +
> > +		snprintf(res_name, sizeof(res_name), "port%d", port);
> > +
> > +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> > +						   res_name);
> > +		target = ocelot_regmap_init(ocelot, res);
> > +		if (IS_ERR(target))
> > +			continue;
> > +
> > +		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> > +		if (!phy_node)
> > +			continue;
> > +
> > +		phy = of_phy_find_device(phy_node);
> > +		of_node_put(phy_node);
> > +		if (!phy)
> > +			continue;
> > +
> > +		err = ocelot_probe_port(ocelot, port, target, phy);
> > +		if (err) {
> > +			of_node_put(portnp);
> > +			return err;
> > +		}
> > +
> > +		ocelot_port = ocelot->ports[port];
> > +		priv = container_of(ocelot_port, struct ocelot_port_private,
> > +				    port);
> > +
> > +		of_get_phy_mode(portnp, &phy_mode);
> > +
> > +		ocelot_port->phy_mode = phy_mode;
> > +
> > +		switch (ocelot_port->phy_mode) {
> > +		case PHY_INTERFACE_MODE_NA:
> > +			continue;
> > +		case PHY_INTERFACE_MODE_SGMII:
> > +			break;
> > +		case PHY_INTERFACE_MODE_QSGMII:
> > +			/* Ensure clock signals and speed is set on all
> > +			 * QSGMII links
> > +			 */
> > +			ocelot_port_writel(ocelot_port,
> > +					   DEV_CLOCK_CFG_LINK_SPEED
> > +					   (OCELOT_SPEED_1000),
> > +					   DEV_CLOCK_CFG);
> > +			break;
> > +		default:
> > +			dev_err(ocelot->dev,
> > +				"invalid phy mode for port%d, (Q)SGMII only\n",
> > +				port);
> > +			of_node_put(portnp);
> > +			return -EINVAL;
> > +		}
> > +
> > +		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> > +		if (IS_ERR(serdes)) {
> > +			err = PTR_ERR(serdes);
> > +			if (err == -EPROBE_DEFER)
> > +				dev_dbg(ocelot->dev, "deferring probe\n");
> > +			else
> > +				dev_err(ocelot->dev,
> > +					"missing SerDes phys for port%d\n",
> > +					port);
> > +
> > +			of_node_put(portnp);
> > +			return err;
> > +		}
> > +
> > +		priv->serdes = serdes;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  static int mscc_ocelot_probe(struct platform_device *pdev)
> >  {
> >  	struct device_node *np = pdev->dev.of_node;
> > -	struct device_node *ports, *portnp;
> >  	int err, irq_xtr, irq_ptp_rdy;
> > +	struct device_node *ports;
> >  	struct ocelot *ocelot;
> >  	struct regmap *hsio;
> >  	unsigned int i;
> > @@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
> >  
> >  	ports = of_get_child_by_name(np, "ethernet-ports");
> >  	if (!ports) {
> > -		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
> > +		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
> >  		return -ENODEV;
> >  	}
> >  
> >  	ocelot->num_phys_ports = of_get_child_count(ports);
> >  
> > -	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> > -				     sizeof(struct ocelot_port *), GFP_KERNEL);
> > -	if (!ocelot->ports) {
> > -		err = -ENOMEM;
> > -		goto out_put_ports;
> > -	}
> > -
> >  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
> >  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> >  	ocelot->vcap = vsc7514_vcap_props;
> > @@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
> >  	if (err)
> >  		goto out_put_ports;
> >  
> > +	err = mscc_ocelot_init_ports(pdev, ports);
> > +	if (err)
> > +		goto out_put_ports;
> > +
> >  	if (ocelot->ptp) {
> >  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
> >  		if (err) {
> > @@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
> >  		}
> >  	}
> >  
> > -	/* No NPI port */
> > -	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> > -			     OCELOT_TAG_PREFIX_NONE);
> > -
> > -	for_each_available_child_of_node(ports, portnp) {
> > -		struct ocelot_port_private *priv;
> > -		struct ocelot_port *ocelot_port;
> > -		struct device_node *phy_node;
> > -		phy_interface_t phy_mode;
> > -		struct phy_device *phy;
> > -		struct regmap *target;
> > -		struct resource *res;
> > -		struct phy *serdes;
> > -		char res_name[8];
> > -		u32 port;
> > -
> > -		if (of_property_read_u32(portnp, "reg", &port))
> > -			continue;
> > -
> > -		snprintf(res_name, sizeof(res_name), "port%d", port);
> > -
> > -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> > -						   res_name);
> > -		target = ocelot_regmap_init(ocelot, res);
> > -		if (IS_ERR(target))
> > -			continue;
> > -
> > -		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> > -		if (!phy_node)
> > -			continue;
> > -
> > -		phy = of_phy_find_device(phy_node);
> > -		of_node_put(phy_node);
> > -		if (!phy)
> > -			continue;
> > -
> > -		err = ocelot_probe_port(ocelot, port, target, phy);
> > -		if (err) {
> > -			of_node_put(portnp);
> > -			goto out_put_ports;
> > -		}
> > -
> > -		ocelot_port = ocelot->ports[port];
> > -		priv = container_of(ocelot_port, struct ocelot_port_private,
> > -				    port);
> > -
> > -		of_get_phy_mode(portnp, &phy_mode);
> > -
> > -		ocelot_port->phy_mode = phy_mode;
> > -
> > -		switch (ocelot_port->phy_mode) {
> > -		case PHY_INTERFACE_MODE_NA:
> > -			continue;
> > -		case PHY_INTERFACE_MODE_SGMII:
> > -			break;
> > -		case PHY_INTERFACE_MODE_QSGMII:
> > -			/* Ensure clock signals and speed is set on all
> > -			 * QSGMII links
> > -			 */
> > -			ocelot_port_writel(ocelot_port,
> > -					   DEV_CLOCK_CFG_LINK_SPEED
> > -					   (OCELOT_SPEED_1000),
> > -					   DEV_CLOCK_CFG);
> > -			break;
> > -		default:
> > -			dev_err(ocelot->dev,
> > -				"invalid phy mode for port%d, (Q)SGMII only\n",
> > -				port);
> > -			of_node_put(portnp);
> > -			err = -EINVAL;
> > -			goto out_put_ports;
> > -		}
> > -
> > -		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> > -		if (IS_ERR(serdes)) {
> > -			err = PTR_ERR(serdes);
> > -			if (err == -EPROBE_DEFER)
> > -				dev_dbg(ocelot->dev, "deferring probe\n");
> > -			else
> > -				dev_err(ocelot->dev,
> > -					"missing SerDes phys for port%d\n",
> > -					port);
> > -
> > -			of_node_put(portnp);
> > -			goto out_put_ports;
> > -		}
> > -
> > -		priv->serdes = serdes;
> > -	}
> > -
> >  	register_netdevice_notifier(&ocelot_netdevice_nb);
> >  	register_switchdev_notifier(&ocelot_switchdev_nb);
> >  	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> > -- 
> > 2.25.1
> > 
> 
> On 18/09/2020 04:07:29+0300, Vladimir Oltean wrote:
> > From: Vladimir Oltean <vladimir.oltean@nxp.com>
> > 
> > This driver was not unregistering its network interfaces on unbind.
> > Now it is.
> > 
> > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> > Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> > ---
> > Changes in v2:
> > No longer call mscc_ocelot_release_ports from the regular exit path of
> > mscc_ocelot_init_ports, which was incorrect.
> > 
> >  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
> >  1 file changed, 21 insertions(+)
> > 
> > diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > index ff4a01424953..252c49b5f22b 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > @@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
> >  	.enable		= ocelot_ptp_enable,
> >  };
> >  
> > +static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> > +{
> > +	int port;
> > +
> > +	for (port = 0; port < ocelot->num_phys_ports; port++) {
> > +		struct ocelot_port_private *priv;
> > +		struct ocelot_port *ocelot_port;
> > +
> > +		ocelot_port = ocelot->ports[port];
> > +		if (!ocelot_port)
> > +			continue;
> > +
> > +		priv = container_of(ocelot_port, struct ocelot_port_private,
> > +				    port);
> > +
> > +		unregister_netdev(priv->dev);
> > +		free_netdev(priv->dev);
> > +	}
> > +}
> > +
> >  static int mscc_ocelot_init_ports(struct platform_device *pdev,
> >  				  struct device_node *ports)
> >  {
> > @@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
> >  	struct ocelot *ocelot = platform_get_drvdata(pdev);
> >  
> >  	ocelot_deinit_timestamp(ocelot);
> > +	mscc_ocelot_release_ports(ocelot);
> >  	ocelot_deinit(ocelot);
> >  	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> >  	unregister_switchdev_notifier(&ocelot_switchdev_nb);
> > -- 
> > 2.25.1
> > 
> 
> On 18/09/2020 04:07:30+0300, Vladimir Oltean wrote:
> > From: Vladimir Oltean <vladimir.oltean@nxp.com>
> > 
> > Currently mscc_ocelot_init_ports() will skip initializing a port when it
> > doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> > NULL. Take this into consideration when tearing down the driver, and add
> > a new function ocelot_deinit_port() to the switch library, mirror of
> > ocelot_init_port(), which needs to be called by the driver for all ports
> > it has initialized.
> > 
> > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> > ---
> > Changes in v2:
> > Patch is new.
> > 
> >  drivers/net/dsa/ocelot/felix.c             |  3 +++
> >  drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
> >  drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
> >  include/soc/mscc/ocelot.h                  |  1 +
> >  4 files changed, 14 insertions(+), 8 deletions(-)
> > 
> > diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> > index f7b43f8d56ed..64939ee14648 100644
> > --- a/drivers/net/dsa/ocelot/felix.c
> > +++ b/drivers/net/dsa/ocelot/felix.c
> > @@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
> >  {
> >  	struct ocelot *ocelot = ds->priv;
> >  	struct felix *felix = ocelot_to_felix(ocelot);
> > +	int port;
> >  
> >  	if (felix->info->mdio_bus_free)
> >  		felix->info->mdio_bus_free(ocelot);
> >  
> > +	for (port = 0; port < ocelot->num_phys_ports; port++)
> > +		ocelot_deinit_port(ocelot, port);
> >  	ocelot_deinit_timestamp(ocelot);
> >  	/* stop workqueue thread */
> >  	ocelot_deinit(ocelot);
> > diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> > index 83eb7c325061..8518e1d60da4 100644
> > --- a/drivers/net/ethernet/mscc/ocelot.c
> > +++ b/drivers/net/ethernet/mscc/ocelot.c
> > @@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
> >  
> >  void ocelot_deinit(struct ocelot *ocelot)
> >  {
> > -	struct ocelot_port *port;
> > -	int i;
> > -
> >  	cancel_delayed_work(&ocelot->stats_work);
> >  	destroy_workqueue(ocelot->stats_queue);
> >  	mutex_destroy(&ocelot->stats_lock);
> > -
> > -	for (i = 0; i < ocelot->num_phys_ports; i++) {
> > -		port = ocelot->ports[i];
> > -		skb_queue_purge(&port->tx_skbs);
> > -	}
> >  }
> >  EXPORT_SYMBOL(ocelot_deinit);
> >  
> > +void ocelot_deinit_port(struct ocelot *ocelot, int port)
> > +{
> > +	struct ocelot_port *ocelot_port = ocelot->ports[port];
> > +
> > +	skb_queue_purge(&ocelot_port->tx_skbs);
> > +}
> > +EXPORT_SYMBOL(ocelot_deinit_port);
> > +
> >  MODULE_LICENSE("Dual MIT/GPL");
> > diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > index 252c49b5f22b..e02fb8bfab63 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > @@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> >  		if (!ocelot_port)
> >  			continue;
> >  
> > +		ocelot_deinit_port(ocelot, port);
> > +
> >  		priv = container_of(ocelot_port, struct ocelot_port_private,
> >  				    port);
> >  
> > diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> > index 4521dd602ddc..0ac4e7fba086 100644
> > --- a/include/soc/mscc/ocelot.h
> > +++ b/include/soc/mscc/ocelot.h
> > @@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
> >  int ocelot_init(struct ocelot *ocelot);
> >  void ocelot_deinit(struct ocelot *ocelot);
> >  void ocelot_init_port(struct ocelot *ocelot, int port);
> > +void ocelot_deinit_port(struct ocelot *ocelot, int port);
> >  
> >  /* DSA callbacks */
> >  void ocelot_port_enable(struct ocelot *ocelot, int port,
> > -- 
> > 2.25.1
> > 
> 
> 
> -- 
> Alexandre Belloni, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 2/8] net: mscc: ocelot: add locking for the port TX timestamp ID
  2020-09-18  1:07 ` [PATCH v2 net 2/8] net: mscc: ocelot: add locking for the port TX timestamp ID Vladimir Oltean
  2020-09-18  2:37   ` Florian Fainelli
@ 2020-09-18 15:27   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:27 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:24+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The ocelot_port->ts_id is used to:
> (a) populate skb->cb[0] for matching the TX timestamp in the PTP IRQ
>     with an skb.
> (b) populate the REW_OP from the injection header of the ongoing skb.
> Only then is ocelot_port->ts_id incremented.
> 
> This is a problem because, at least theoretically, another timestampable
> skb might use the same ocelot_port->ts_id before that is incremented.
> Normally all transmit calls are serialized by the netdev transmit
> spinlock, but in this case, ocelot_port_add_txtstamp_skb() is also
> called by DSA, which has started declaring the NETIF_F_LLTX feature
> since commit 2b86cb829976 ("net: dsa: declare lockless TX feature for
> slave ports").  So the logic of using and incrementing the timestamp id
> should be atomic per port.
> 
> The solution is to use the global ocelot_port->ts_id only while
> protected by the associated ocelot_port->ts_id_lock. That's where we
> populate skb->cb[0]. Note that for ocelot, ocelot_port_add_txtstamp_skb
> is called for the actual skb, but for felix, it is called for the skb's
> clone. That is something which will also be changed in the future.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> Added an extra explanation about NETIF_F_LLTX in commit message.
> 
>  drivers/net/ethernet/mscc/ocelot.c     |  8 +++++++-
>  drivers/net/ethernet/mscc/ocelot_net.c |  6 ++----
>  include/soc/mscc/ocelot.h              |  1 +
>  net/dsa/tag_ocelot.c                   | 11 +++++++----
>  4 files changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 5abb7d2b0a9e..83eb7c325061 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
>  	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> +		spin_lock(&ocelot_port->ts_id_lock);
> +
>  		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
>  		/* Store timestamp ID in cb[0] of sk_buff */
> -		skb->cb[0] = ocelot_port->ts_id % 4;
> +		skb->cb[0] = ocelot_port->ts_id;
> +		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
>  		skb_queue_tail(&ocelot_port->tx_skbs, skb);
> +
> +		spin_unlock(&ocelot_port->ts_id_lock);
>  		return 0;
>  	}
>  	return -ENODATA;
> @@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
>  	struct ocelot_port *ocelot_port = ocelot->ports[port];
>  
>  	skb_queue_head_init(&ocelot_port->tx_skbs);
> +	spin_lock_init(&ocelot_port->ts_id_lock);
>  
>  	/* Basic L2 initialization */
>  
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index cacabc23215a..8490e42e9e2d 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -349,10 +349,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
>  
>  	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
>  		info.rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			info.rew_op |= skb->cb[0] << 3;
>  	}
>  
>  	ocelot_gen_ifh(ifh, &info);
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index da369b12005f..4521dd602ddc 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -566,6 +566,7 @@ struct ocelot_port {
>  	u8				ptp_cmd;
>  	struct sk_buff_head		tx_skbs;
>  	u8				ts_id;
> +	spinlock_t			ts_id_lock;
>  
>  	phy_interface_t			phy_mode;
>  
> diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
> index 42f327c06dca..b4fc05cafaa6 100644
> --- a/net/dsa/tag_ocelot.c
> +++ b/net/dsa/tag_ocelot.c
> @@ -160,11 +160,14 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
>  	packing(injection, &qos_class, 19,  17, OCELOT_TAG_LEN, PACK, 0);
>  
>  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
> +		struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
> +
>  		rew_op = ocelot_port->ptp_cmd;
> -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
> -			rew_op |= (ocelot_port->ts_id  % 4) << 3;
> -			ocelot_port->ts_id++;
> -		}
> +		/* Retrieve timestamp ID populated inside skb->cb[0] of the
> +		 * clone by ocelot_port_add_txtstamp_skb
> +		 */
> +		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
> +			rew_op |= clone->cb[0] << 3;
>  
>  		packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
>  	}
> -- 
> 2.25.1
> 

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 3/8] net: dsa: seville: fix buffer size of the queue system
  2020-09-18  1:07 ` [PATCH v2 net 3/8] net: dsa: seville: fix buffer size of the queue system Vladimir Oltean
  2020-09-18  2:37   ` Florian Fainelli
@ 2020-09-18 15:27   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:27 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:25+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> The VSC9953 Seville switch has 2 megabits of buffer split into 4360
> words of 60 bytes each.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> None.
> 
>  drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 2d6a5f5758f8..83a1ab9393e9 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1018,7 +1018,7 @@ static const struct felix_info seville_info_vsc9953 = {
>  	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
>  	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
>  	.vcap			= vsc9953_vcap_props,
> -	.shared_queue_sz	= 128 * 1024,
> +	.shared_queue_sz	= 2048 * 1024,
>  	.num_mact_rows		= 2048,
>  	.num_ports		= 10,
>  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
> -- 
> 2.25.1
> 

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 4/8] net: mscc: ocelot: check for errors on memory allocation of ports
  2020-09-18  1:07 ` [PATCH v2 net 4/8] net: mscc: ocelot: check for errors on memory allocation of ports Vladimir Oltean
  2020-09-18  2:35   ` Florian Fainelli
@ 2020-09-18 15:27   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:27 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:26+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Do not proceed probing if we couldn't allocate memory for the ports
> array, just error out.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 65408bc994c4..904ea299a5e8 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -993,6 +993,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
>  				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports) {
> +		err = -ENOMEM;
> +		goto out_put_ports;
> +	}
>  
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
> -- 
> 2.25.1
> 

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 5/8] net: mscc: ocelot: error checking when calling ocelot_init()
  2020-09-18  1:07 ` [PATCH v2 net 5/8] net: mscc: ocelot: error checking when calling ocelot_init() Vladimir Oltean
  2020-09-18  2:35   ` Florian Fainelli
@ 2020-09-18 15:28   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:28 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:27+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> ocelot_init() allocates memory, resets the switch and polls for a status
> register, things which can fail. Stop probing the driver in that case,
> and propagate the error result.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> Stopped leaking the 'ports' OF node in the VSC7514 driver.
> 
>  drivers/net/dsa/ocelot/felix.c             | 5 ++++-
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ++++-
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index a1e1d3824110..f7b43f8d56ed 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -571,7 +571,10 @@ static int felix_setup(struct dsa_switch *ds)
>  	if (err)
>  		return err;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		return err;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 904ea299a5e8..a1cbb20a7757 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -1002,7 +1002,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
>  
> -	ocelot_init(ocelot);
> +	err = ocelot_init(ocelot);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> -- 
> 2.25.1
> 

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 6/8] net: mscc: ocelot: refactor ports parsing code into a dedicated function
  2020-09-18  1:07 ` [PATCH v2 net 6/8] net: mscc: ocelot: refactor ports parsing code into a dedicated function Vladimir Oltean
  2020-09-18  2:34   ` Florian Fainelli
@ 2020-09-18 15:28   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:28 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:28+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> mscc_ocelot_probe() is already pretty large and hard to follow. So move
> the code for parsing ports in a separate function.
> 
> This makes it easier for the next patch to just call
> mscc_ocelot_release_ports from the error path of mscc_ocelot_init_ports.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> Keep a reference to the 'ports' OF node at caller side, in
> mscc_ocelot_probe, because we need to populate ocelot->num_phys_ports
> early. The ocelot_init() function depends on it being set correctly.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 209 +++++++++++----------
>  1 file changed, 110 insertions(+), 99 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index a1cbb20a7757..ff4a01424953 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,11 +896,115 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static int mscc_ocelot_init_ports(struct platform_device *pdev,
> +				  struct device_node *ports)
> +{
> +	struct ocelot *ocelot = platform_get_drvdata(pdev);
> +	struct device_node *portnp;
> +	int err;
> +
> +	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
> +				     sizeof(struct ocelot_port *), GFP_KERNEL);
> +	if (!ocelot->ports)
> +		return -ENOMEM;
> +
> +	/* No NPI port */
> +	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> +			     OCELOT_TAG_PREFIX_NONE);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +		struct device_node *phy_node;
> +		phy_interface_t phy_mode;
> +		struct phy_device *phy;
> +		struct regmap *target;
> +		struct resource *res;
> +		struct phy *serdes;
> +		char res_name[8];
> +		u32 port;
> +
> +		if (of_property_read_u32(portnp, "reg", &port))
> +			continue;
> +
> +		snprintf(res_name, sizeof(res_name), "port%d", port);
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						   res_name);
> +		target = ocelot_regmap_init(ocelot, res);
> +		if (IS_ERR(target))
> +			continue;
> +
> +		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> +		if (!phy_node)
> +			continue;
> +
> +		phy = of_phy_find_device(phy_node);
> +		of_node_put(phy_node);
> +		if (!phy)
> +			continue;
> +
> +		err = ocelot_probe_port(ocelot, port, target, phy);
> +		if (err) {
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		ocelot_port = ocelot->ports[port];
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		of_get_phy_mode(portnp, &phy_mode);
> +
> +		ocelot_port->phy_mode = phy_mode;
> +
> +		switch (ocelot_port->phy_mode) {
> +		case PHY_INTERFACE_MODE_NA:
> +			continue;
> +		case PHY_INTERFACE_MODE_SGMII:
> +			break;
> +		case PHY_INTERFACE_MODE_QSGMII:
> +			/* Ensure clock signals and speed is set on all
> +			 * QSGMII links
> +			 */
> +			ocelot_port_writel(ocelot_port,
> +					   DEV_CLOCK_CFG_LINK_SPEED
> +					   (OCELOT_SPEED_1000),
> +					   DEV_CLOCK_CFG);
> +			break;
> +		default:
> +			dev_err(ocelot->dev,
> +				"invalid phy mode for port%d, (Q)SGMII only\n",
> +				port);
> +			of_node_put(portnp);
> +			return -EINVAL;
> +		}
> +
> +		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err == -EPROBE_DEFER)
> +				dev_dbg(ocelot->dev, "deferring probe\n");
> +			else
> +				dev_err(ocelot->dev,
> +					"missing SerDes phys for port%d\n",
> +					port);
> +
> +			of_node_put(portnp);
> +			return err;
> +		}
> +
> +		priv->serdes = serdes;
> +	}
> +
> +	return 0;
> +}
> +
>  static int mscc_ocelot_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> -	struct device_node *ports, *portnp;
>  	int err, irq_xtr, irq_ptp_rdy;
> +	struct device_node *ports;
>  	struct ocelot *ocelot;
>  	struct regmap *hsio;
>  	unsigned int i;
> @@ -985,19 +1089,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  
>  	ports = of_get_child_by_name(np, "ethernet-ports");
>  	if (!ports) {
> -		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
> +		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
>  		return -ENODEV;
>  	}
>  
>  	ocelot->num_phys_ports = of_get_child_count(ports);
>  
> -	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
> -				     sizeof(struct ocelot_port *), GFP_KERNEL);
> -	if (!ocelot->ports) {
> -		err = -ENOMEM;
> -		goto out_put_ports;
> -	}
> -
>  	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
>  	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
>  	ocelot->vcap = vsc7514_vcap_props;
> @@ -1006,6 +1103,10 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  	if (err)
>  		goto out_put_ports;
>  
> +	err = mscc_ocelot_init_ports(pdev, ports);
> +	if (err)
> +		goto out_put_ports;
> +
>  	if (ocelot->ptp) {
>  		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
>  		if (err) {
> @@ -1015,96 +1116,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	/* No NPI port */
> -	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
> -			     OCELOT_TAG_PREFIX_NONE);
> -
> -	for_each_available_child_of_node(ports, portnp) {
> -		struct ocelot_port_private *priv;
> -		struct ocelot_port *ocelot_port;
> -		struct device_node *phy_node;
> -		phy_interface_t phy_mode;
> -		struct phy_device *phy;
> -		struct regmap *target;
> -		struct resource *res;
> -		struct phy *serdes;
> -		char res_name[8];
> -		u32 port;
> -
> -		if (of_property_read_u32(portnp, "reg", &port))
> -			continue;
> -
> -		snprintf(res_name, sizeof(res_name), "port%d", port);
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> -						   res_name);
> -		target = ocelot_regmap_init(ocelot, res);
> -		if (IS_ERR(target))
> -			continue;
> -
> -		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
> -		if (!phy_node)
> -			continue;
> -
> -		phy = of_phy_find_device(phy_node);
> -		of_node_put(phy_node);
> -		if (!phy)
> -			continue;
> -
> -		err = ocelot_probe_port(ocelot, port, target, phy);
> -		if (err) {
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		ocelot_port = ocelot->ports[port];
> -		priv = container_of(ocelot_port, struct ocelot_port_private,
> -				    port);
> -
> -		of_get_phy_mode(portnp, &phy_mode);
> -
> -		ocelot_port->phy_mode = phy_mode;
> -
> -		switch (ocelot_port->phy_mode) {
> -		case PHY_INTERFACE_MODE_NA:
> -			continue;
> -		case PHY_INTERFACE_MODE_SGMII:
> -			break;
> -		case PHY_INTERFACE_MODE_QSGMII:
> -			/* Ensure clock signals and speed is set on all
> -			 * QSGMII links
> -			 */
> -			ocelot_port_writel(ocelot_port,
> -					   DEV_CLOCK_CFG_LINK_SPEED
> -					   (OCELOT_SPEED_1000),
> -					   DEV_CLOCK_CFG);
> -			break;
> -		default:
> -			dev_err(ocelot->dev,
> -				"invalid phy mode for port%d, (Q)SGMII only\n",
> -				port);
> -			of_node_put(portnp);
> -			err = -EINVAL;
> -			goto out_put_ports;
> -		}
> -
> -		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
> -		if (IS_ERR(serdes)) {
> -			err = PTR_ERR(serdes);
> -			if (err == -EPROBE_DEFER)
> -				dev_dbg(ocelot->dev, "deferring probe\n");
> -			else
> -				dev_err(ocelot->dev,
> -					"missing SerDes phys for port%d\n",
> -					port);
> -
> -			of_node_put(portnp);
> -			goto out_put_ports;
> -		}
> -
> -		priv->serdes = serdes;
> -	}
> -
>  	register_netdevice_notifier(&ocelot_netdevice_nb);
>  	register_switchdev_notifier(&ocelot_switchdev_nb);
>  	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> -- 
> 2.25.1
> 

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 7/8] net: mscc: ocelot: unregister net devices on unbind
  2020-09-18  1:07 ` [PATCH v2 net 7/8] net: mscc: ocelot: unregister net devices on unbind Vladimir Oltean
  2020-09-18  2:34   ` Florian Fainelli
@ 2020-09-18 15:28   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:28 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:29+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This driver was not unregistering its network interfaces on unbind.
> Now it is.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> No longer call mscc_ocelot_release_ports from the regular exit path of
> mscc_ocelot_init_ports, which was incorrect.
> 
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index ff4a01424953..252c49b5f22b 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -896,6 +896,26 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
>  	.enable		= ocelot_ptp_enable,
>  };
>  
> +static void mscc_ocelot_release_ports(struct ocelot *ocelot)
> +{
> +	int port;
> +
> +	for (port = 0; port < ocelot->num_phys_ports; port++) {
> +		struct ocelot_port_private *priv;
> +		struct ocelot_port *ocelot_port;
> +
> +		ocelot_port = ocelot->ports[port];
> +		if (!ocelot_port)
> +			continue;
> +
> +		priv = container_of(ocelot_port, struct ocelot_port_private,
> +				    port);
> +
> +		unregister_netdev(priv->dev);
> +		free_netdev(priv->dev);
> +	}
> +}
> +
>  static int mscc_ocelot_init_ports(struct platform_device *pdev,
>  				  struct device_node *ports)
>  {
> @@ -1132,6 +1152,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
>  	struct ocelot *ocelot = platform_get_drvdata(pdev);
>  
>  	ocelot_deinit_timestamp(ocelot);
> +	mscc_ocelot_release_ports(ocelot);
>  	ocelot_deinit(ocelot);
>  	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
>  	unregister_switchdev_notifier(&ocelot_switchdev_nb);
> -- 
> 2.25.1
> 

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 8/8] net: mscc: ocelot: deinitialize only initialized ports
  2020-09-18  1:07 ` [PATCH v2 net 8/8] net: mscc: ocelot: deinitialize only initialized ports Vladimir Oltean
  2020-09-18  2:36   ` Florian Fainelli
@ 2020-09-18 15:29   ` Alexandre Belloni
  1 sibling, 0 replies; 33+ messages in thread
From: Alexandre Belloni @ 2020-09-18 15:29 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: davem, netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, andrew, vivien.didelot, f.fainelli, kuba

On 18/09/2020 04:07:30+0300, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> Currently mscc_ocelot_init_ports() will skip initializing a port when it
> doesn't have a phy-handle, so the ocelot->ports[port] pointer will be
> NULL. Take this into consideration when tearing down the driver, and add
> a new function ocelot_deinit_port() to the switch library, mirror of
> ocelot_init_port(), which needs to be called by the driver for all ports
> it has initialized.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>

Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

> ---
> Changes in v2:
> Patch is new.
> 
>  drivers/net/dsa/ocelot/felix.c             |  3 +++
>  drivers/net/ethernet/mscc/ocelot.c         | 16 ++++++++--------
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |  2 ++
>  include/soc/mscc/ocelot.h                  |  1 +
>  4 files changed, 14 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index f7b43f8d56ed..64939ee14648 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -624,10 +624,13 @@ static void felix_teardown(struct dsa_switch *ds)
>  {
>  	struct ocelot *ocelot = ds->priv;
>  	struct felix *felix = ocelot_to_felix(ocelot);
> +	int port;
>  
>  	if (felix->info->mdio_bus_free)
>  		felix->info->mdio_bus_free(ocelot);
>  
> +	for (port = 0; port < ocelot->num_phys_ports; port++)
> +		ocelot_deinit_port(ocelot, port);
>  	ocelot_deinit_timestamp(ocelot);
>  	/* stop workqueue thread */
>  	ocelot_deinit(ocelot);
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 83eb7c325061..8518e1d60da4 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -1550,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);
>  
>  void ocelot_deinit(struct ocelot *ocelot)
>  {
> -	struct ocelot_port *port;
> -	int i;
> -
>  	cancel_delayed_work(&ocelot->stats_work);
>  	destroy_workqueue(ocelot->stats_queue);
>  	mutex_destroy(&ocelot->stats_lock);
> -
> -	for (i = 0; i < ocelot->num_phys_ports; i++) {
> -		port = ocelot->ports[i];
> -		skb_queue_purge(&port->tx_skbs);
> -	}
>  }
>  EXPORT_SYMBOL(ocelot_deinit);
>  
> +void ocelot_deinit_port(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_port *ocelot_port = ocelot->ports[port];
> +
> +	skb_queue_purge(&ocelot_port->tx_skbs);
> +}
> +EXPORT_SYMBOL(ocelot_deinit_port);
> +
>  MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 252c49b5f22b..e02fb8bfab63 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -908,6 +908,8 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
>  		if (!ocelot_port)
>  			continue;
>  
> +		ocelot_deinit_port(ocelot, port);
> +
>  		priv = container_of(ocelot_port, struct ocelot_port_private,
>  				    port);
>  
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 4521dd602ddc..0ac4e7fba086 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -678,6 +678,7 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
>  int ocelot_init(struct ocelot *ocelot);
>  void ocelot_deinit(struct ocelot *ocelot);
>  void ocelot_init_port(struct ocelot *ocelot, int port);
> +void ocelot_deinit_port(struct ocelot *ocelot, int port);
>  
>  /* DSA callbacks */
>  void ocelot_port_enable(struct ocelot *ocelot, int port,
> -- 
> 2.25.1
> 

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver
  2020-09-18  1:07 [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver Vladimir Oltean
                   ` (7 preceding siblings ...)
  2020-09-18  1:07 ` [PATCH v2 net 8/8] net: mscc: ocelot: deinitialize only initialized ports Vladimir Oltean
@ 2020-09-18 20:52 ` David Miller
  8 siblings, 0 replies; 33+ messages in thread
From: David Miller @ 2020-09-18 20:52 UTC (permalink / raw)
  To: olteanv
  Cc: netdev, yangbo.lu, xiaoliang.yang_1, UNGLinuxDriver,
	claudiu.manoil, alexandre.belloni, andrew, vivien.didelot,
	f.fainelli, kuba

From: Vladimir Oltean <olteanv@gmail.com>
Date: Fri, 18 Sep 2020 04:07:22 +0300

> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> This is a series of 8 assorted patches for "net", on the drivers for the
> VSC7514 MIPS switch (Ocelot-1), the VSC9953 PowerPC (Seville), and a few
> more that are common to all supported devices since they are in the
> common library portion.

Series applied, thanks.

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2020-09-18 20:53 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-18  1:07 [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver Vladimir Oltean
2020-09-18  1:07 ` [PATCH v2 net 1/8] net: mscc: ocelot: fix race condition with TX timestamping Vladimir Oltean
2020-09-18 15:23   ` Alexandre Belloni
2020-09-18 15:24   ` Alexandre Belloni
2020-09-18 15:24   ` Alexandre Belloni
2020-09-18 15:24   ` Alexandre Belloni
2020-09-18 15:24   ` Alexandre Belloni
2020-09-18 15:25   ` Alexandre Belloni
2020-09-18 15:25   ` Alexandre Belloni
2020-09-18 15:26   ` Alexandre Belloni
2020-09-18 15:27     ` Alexandre Belloni
2020-09-18  1:07 ` [PATCH v2 net 2/8] net: mscc: ocelot: add locking for the port TX timestamp ID Vladimir Oltean
2020-09-18  2:37   ` Florian Fainelli
2020-09-18 15:27   ` Alexandre Belloni
2020-09-18  1:07 ` [PATCH v2 net 3/8] net: dsa: seville: fix buffer size of the queue system Vladimir Oltean
2020-09-18  2:37   ` Florian Fainelli
2020-09-18 15:27   ` Alexandre Belloni
2020-09-18  1:07 ` [PATCH v2 net 4/8] net: mscc: ocelot: check for errors on memory allocation of ports Vladimir Oltean
2020-09-18  2:35   ` Florian Fainelli
2020-09-18 15:27   ` Alexandre Belloni
2020-09-18  1:07 ` [PATCH v2 net 5/8] net: mscc: ocelot: error checking when calling ocelot_init() Vladimir Oltean
2020-09-18  2:35   ` Florian Fainelli
2020-09-18 15:28   ` Alexandre Belloni
2020-09-18  1:07 ` [PATCH v2 net 6/8] net: mscc: ocelot: refactor ports parsing code into a dedicated function Vladimir Oltean
2020-09-18  2:34   ` Florian Fainelli
2020-09-18 15:28   ` Alexandre Belloni
2020-09-18  1:07 ` [PATCH v2 net 7/8] net: mscc: ocelot: unregister net devices on unbind Vladimir Oltean
2020-09-18  2:34   ` Florian Fainelli
2020-09-18 15:28   ` Alexandre Belloni
2020-09-18  1:07 ` [PATCH v2 net 8/8] net: mscc: ocelot: deinitialize only initialized ports Vladimir Oltean
2020-09-18  2:36   ` Florian Fainelli
2020-09-18 15:29   ` Alexandre Belloni
2020-09-18 20:52 ` [PATCH v2 net 0/8] Bugfixes in Microsemi Ocelot switch driver David Miller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.