* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).