On 18/09/2020 04:07:23+0300, Vladimir Oltean wrote: > From: Vladimir Oltean <vladimir.olt...@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.olt...@nxp.com> > Reviewed-by: Horatiu Vultur <horatiu.vul...@microchip.com>
Tested-by: Alexandre Belloni <alexandre.bell...@bootlin.com> Reviewed-by: Alexandre Belloni <alexandre.bell...@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.olt...@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.olt...@nxp.com> > Reviewed-by: Horatiu Vultur <horatiu.vul...@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.olt...@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.olt...@nxp.com> > Reviewed-by: Horatiu Vultur <horatiu.vul...@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.olt...@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.olt...@nxp.com> > Reviewed-by: Horatiu Vultur <horatiu.vul...@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.olt...@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.olt...@nxp.com> > Reviewed-by: Horatiu Vultur <horatiu.vul...@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.olt...@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.olt...@nxp.com> > Reviewed-by: Horatiu Vultur <horatiu.vul...@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.olt...@nxp.com> > > This driver was not unregistering its network interfaces on unbind. > Now it is. > > Signed-off-by: Vladimir Oltean <vladimir.olt...@nxp.com> > Reviewed-by: Horatiu Vultur <horatiu.vul...@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.olt...@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.olt...@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