/* When we manage the MAC/PHY using phylink there is no need
         * to manually update the netif_carrier.
+        * We can avoid locking because we are called from the "link changed"
+        * IRQ handler, which is the same as the "endpoint changed" IRQ handler
+        * (the writer to priv->mac), so we cannot race with it.
         */
-       if (dpaa2_eth_is_type_phy(priv))
+       if (dpaa2_mac_is_type_phy(priv->mac))
                goto out;
 
        /* Chech link state; speed / duplex changes are not treated yet */
 
        dpaa2_eth_seed_pools(priv);
 
+       mutex_lock(&priv->mac_lock);
+
        if (!dpaa2_eth_is_type_phy(priv)) {
                /* We'll only start the txqs when the link is actually ready;
                 * make sure we don't race against the link up notification,
 
        err = dpni_enable(priv->mc_io, 0, priv->mc_token);
        if (err < 0) {
+               mutex_unlock(&priv->mac_lock);
                netdev_err(net_dev, "dpni_enable() failed\n");
                goto enable_err;
        }
        if (dpaa2_eth_is_type_phy(priv))
                dpaa2_mac_start(priv->mac);
 
+       mutex_unlock(&priv->mac_lock);
+
        return 0;
 
 enable_err:
        int dpni_enabled = 0;
        int retries = 10;
 
+       mutex_lock(&priv->mac_lock);
+
        if (dpaa2_eth_is_type_phy(priv)) {
                dpaa2_mac_stop(priv->mac);
        } else {
                netif_carrier_off(net_dev);
        }
 
+       mutex_unlock(&priv->mac_lock);
+
        /* On dpni_disable(), the MC firmware will:
         * - stop MAC Rx and wait for all Rx frames to be enqueued to software
         * - cut off WRIOP dequeues from egress FQs and wait until transmission
 static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
        struct dpaa2_eth_priv *priv = netdev_priv(dev);
+       int err;
 
        if (cmd == SIOCSHWTSTAMP)
                return dpaa2_eth_ts_ioctl(dev, rq, cmd);
 
-       if (dpaa2_eth_is_type_phy(priv))
-               return phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
+       mutex_lock(&priv->mac_lock);
+
+       if (dpaa2_eth_is_type_phy(priv)) {
+               err = phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
+               mutex_unlock(&priv->mac_lock);
+               return err;
+       }
+
+       mutex_unlock(&priv->mac_lock);
 
        return -EOPNOTSUPP;
 }
                }
        }
 
+       mutex_lock(&priv->mac_lock);
        priv->mac = mac;
+       mutex_unlock(&priv->mac_lock);
 
        return 0;
 
 
 static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
 {
-       struct dpaa2_mac *mac = priv->mac;
+       struct dpaa2_mac *mac;
 
+       mutex_lock(&priv->mac_lock);
+       mac = priv->mac;
        priv->mac = NULL;
+       mutex_unlock(&priv->mac_lock);
 
        if (!mac)
                return;
        struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev);
        struct net_device *net_dev = dev_get_drvdata(dev);
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+       bool had_mac;
        int err;
 
        err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle,
                dpaa2_eth_update_tx_fqids(priv);
 
                rtnl_lock();
-               if (dpaa2_eth_has_mac(priv))
+               /* We can avoid locking because the "endpoint changed" IRQ
+                * handler is the only one who changes priv->mac at runtime,
+                * so we are not racing with anyone.
+                */
+               had_mac = !!priv->mac;
+               if (had_mac)
                        dpaa2_eth_disconnect_mac(priv);
                else
                        dpaa2_eth_connect_mac(priv);
        priv->net_dev = net_dev;
        SET_NETDEV_DEVLINK_PORT(net_dev, &priv->devlink_port);
 
+       mutex_init(&priv->mac_lock);
+
        priv->iommu_domain = iommu_get_domain_for_dev(dev);
 
        priv->tx_tstamp_type = HWTSTAMP_TX_OFF;
 
 static int dpaa2_eth_nway_reset(struct net_device *net_dev)
 {
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+       int err = -EOPNOTSUPP;
+
+       mutex_lock(&priv->mac_lock);
 
        if (dpaa2_eth_is_type_phy(priv))
-               return phylink_ethtool_nway_reset(priv->mac->phylink);
+               err = phylink_ethtool_nway_reset(priv->mac->phylink);
+
+       mutex_unlock(&priv->mac_lock);
 
-       return -EOPNOTSUPP;
+       return err;
 }
 
 static int
                             struct ethtool_link_ksettings *link_settings)
 {
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+       int err;
 
-       if (dpaa2_eth_is_type_phy(priv))
-               return phylink_ethtool_ksettings_get(priv->mac->phylink,
-                                                    link_settings);
+       mutex_lock(&priv->mac_lock);
+
+       if (dpaa2_eth_is_type_phy(priv)) {
+               err = phylink_ethtool_ksettings_get(priv->mac->phylink,
+                                                   link_settings);
+               mutex_unlock(&priv->mac_lock);
+               return err;
+       }
+
+       mutex_unlock(&priv->mac_lock);
 
        link_settings->base.autoneg = AUTONEG_DISABLE;
        if (!(priv->link_state.options & DPNI_LINK_OPT_HALF_DUPLEX))
                             const struct ethtool_link_ksettings *link_settings)
 {
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+       int err = -EOPNOTSUPP;
 
-       if (!dpaa2_eth_is_type_phy(priv))
-               return -EOPNOTSUPP;
+       mutex_lock(&priv->mac_lock);
+
+       if (dpaa2_eth_is_type_phy(priv))
+               err = phylink_ethtool_ksettings_set(priv->mac->phylink,
+                                                   link_settings);
 
-       return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings);
+       mutex_unlock(&priv->mac_lock);
+
+       return err;
 }
 
 static void dpaa2_eth_get_pauseparam(struct net_device *net_dev,
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
        u64 link_options = priv->link_state.options;
 
+       mutex_lock(&priv->mac_lock);
+
        if (dpaa2_eth_is_type_phy(priv)) {
                phylink_ethtool_get_pauseparam(priv->mac->phylink, pause);
+               mutex_unlock(&priv->mac_lock);
                return;
        }
 
+       mutex_unlock(&priv->mac_lock);
+
        pause->rx_pause = dpaa2_eth_rx_pause_enabled(link_options);
        pause->tx_pause = dpaa2_eth_tx_pause_enabled(link_options);
        pause->autoneg = AUTONEG_DISABLE;
                return -EOPNOTSUPP;
        }
 
-       if (dpaa2_eth_is_type_phy(priv))
-               return phylink_ethtool_set_pauseparam(priv->mac->phylink,
-                                                     pause);
+       mutex_lock(&priv->mac_lock);
+
+       if (dpaa2_eth_is_type_phy(priv)) {
+               err = phylink_ethtool_set_pauseparam(priv->mac->phylink,
+                                                    pause);
+               mutex_unlock(&priv->mac_lock);
+               return err;
+       }
+
+       mutex_unlock(&priv->mac_lock);
+
        if (pause->autoneg)
                return -EOPNOTSUPP;
 
        }
        *(data + i++) = buf_cnt_total;
 
+       mutex_lock(&priv->mac_lock);
+
        if (dpaa2_eth_has_mac(priv))
                dpaa2_mac_get_ethtool_stats(priv->mac, data + i);
+
+       mutex_unlock(&priv->mac_lock);
 }
 
 static int dpaa2_eth_prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask,