armvirt: add SFP support patches for NXP Layerscape DPAA2 platforms
[openwrt/openwrt.git] / target / linux / armvirt / patches-6.1 / 701-v6.2-0005-net-dpaa2-eth-assign-priv-mac-after-dpaa2_mac_connec.patch
diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0005-net-dpaa2-eth-assign-priv-mac-after-dpaa2_mac_connec.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0005-net-dpaa2-eth-assign-priv-mac-after-dpaa2_mac_connec.patch
new file mode 100644 (file)
index 0000000..8c7ed6c
--- /dev/null
@@ -0,0 +1,101 @@
+From 06efc9b8a1360cad83cae6e71558e5458cc1fbf3 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Tue, 29 Nov 2022 16:12:14 +0200
+Subject: [PATCH 07/14] net: dpaa2-eth: assign priv->mac after
+ dpaa2_mac_connect() call
+
+There are 2 requirements for correct code:
+
+- Any time the driver accesses the priv->mac pointer at runtime, it
+  either holds NULL to indicate a DPNI-DPNI connection (or unconnected
+  DPNI), or a struct dpaa2_mac whose phylink instance was fully
+  initialized (created and connected to the PHY). No changes are made to
+  priv->mac while it is being used. Currently, rtnl_lock() watches over
+  the call to dpaa2_eth_connect_mac(), so it serves the purpose of
+  serializing this with all readers of priv->mac.
+
+- dpaa2_mac_connect() should run unlocked, because inside it are 2
+  phylink calls with incompatible locking requirements: phylink_create()
+  requires that the rtnl_mutex isn't held, and phylink_fwnode_phy_connect()
+  requires that the rtnl_mutex is held. The only way to solve those
+  contradictory requirements is to let dpaa2_mac_connect() take
+  rtnl_lock() when it needs to.
+
+To solve both requirements, we need to identify the writer side of the
+priv->mac pointer, which can be wrapped in a mutex private to the driver
+in a future patch. The dpaa2_mac_connect() cannot be part of the writer
+side critical section, because of an AB/BA deadlock with rtnl_lock().
+
+So the strategy needs to be that where we prepare the DPMAC by calling
+dpaa2_mac_connect(), and only make priv->mac point to it once it's fully
+prepared. This ensures that the writer side critical section has the
+absolute minimum surface it can.
+
+The reverse strategy is adopted in the dpaa2_eth_disconnect_mac() code
+path. This makes sure that priv->mac is NULL when we start tearing down
+the DPMAC that we disconnected from, and concurrent code will simply not
+see it.
+
+No locking changes in this patch (concurrent code is still blocked by
+the rtnl_mutex).
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
+Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ .../net/ethernet/freescale/dpaa2/dpaa2-eth.c  | 21 +++++++++++--------
+ 1 file changed, 12 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+@@ -4443,9 +4443,8 @@ static int dpaa2_eth_connect_mac(struct
+       err = dpaa2_mac_open(mac);
+       if (err)
+               goto err_free_mac;
+-      priv->mac = mac;
+-      if (dpaa2_eth_is_type_phy(priv)) {
++      if (dpaa2_mac_is_type_phy(mac)) {
+               err = dpaa2_mac_connect(mac);
+               if (err && err != -EPROBE_DEFER)
+                       netdev_err(priv->net_dev, "Error connecting to the MAC endpoint: %pe",
+@@ -4454,11 +4453,12 @@ static int dpaa2_eth_connect_mac(struct
+                       goto err_close_mac;
+       }
++      priv->mac = mac;
++
+       return 0;
+ err_close_mac:
+       dpaa2_mac_close(mac);
+-      priv->mac = NULL;
+ err_free_mac:
+       kfree(mac);
+       return err;
+@@ -4466,15 +4466,18 @@ err_free_mac:
+ static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
+ {
+-      if (dpaa2_eth_is_type_phy(priv))
+-              dpaa2_mac_disconnect(priv->mac);
++      struct dpaa2_mac *mac = priv->mac;
++
++      priv->mac = NULL;
+-      if (!dpaa2_eth_has_mac(priv))
++      if (!mac)
+               return;
+-      dpaa2_mac_close(priv->mac);
+-      kfree(priv->mac);
+-      priv->mac = NULL;
++      if (dpaa2_mac_is_type_phy(mac))
++              dpaa2_mac_disconnect(mac);
++
++      dpaa2_mac_close(mac);
++      kfree(mac);
+ }
+ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)