Author: alfred
Date: Sun Dec 15 07:07:13 2013
New Revision: 259411
URL: http://svnweb.freebsd.org/changeset/base/259411

Log:
  Defer start/stop port to workqueues.
  
  We need to do this because the Linux compat layer uses sx(9) for
  mutex, however the lagg code uses rmlocks and calls into the mellanox
  driver.  This causes deadlock due to sleeping while holding a rmlock.
  
  Submitted by: Shahar Klein (shahark mellanox.com)
  MFC After: 3 days.

Modified:
  head/sys/ofed/drivers/net/mlx4/en_netdev.c
  head/sys/ofed/drivers/net/mlx4/mlx4_en.h

Modified: head/sys/ofed/drivers/net/mlx4/en_netdev.c
==============================================================================
--- head/sys/ofed/drivers/net/mlx4/en_netdev.c  Sun Dec 15 07:04:59 2013        
(r259410)
+++ head/sys/ofed/drivers/net/mlx4/en_netdev.c  Sun Dec 15 07:07:13 2013        
(r259411)
@@ -153,6 +153,19 @@ restart:
        return (i);
 }
 
+static void mlx4_en_stop_port(struct net_device *dev)
+{
+       struct mlx4_en_priv *priv = netdev_priv(dev);
+ 
+       queue_work(priv->mdev->workqueue, &priv->stop_port_task);
+}
+
+static void mlx4_en_start_port(struct net_device *dev)
+{
+       struct mlx4_en_priv *priv = netdev_priv(dev);
+
+       queue_work(priv->mdev->workqueue, &priv->start_port_task);
+}
 
 static void mlx4_en_set_multicast(struct net_device *dev)
 {
@@ -473,6 +486,7 @@ static void mlx4_en_do_get_stats(struct 
 
                queue_delayed_work(mdev->workqueue, &priv->stats_task, 
STATS_DELAY);
        }
+       mlx4_en_QUERY_PORT(priv->mdev, priv->port);
        mutex_unlock(&mdev->state_lock);
 }
 
@@ -498,8 +512,31 @@ static void mlx4_en_linkstate(struct wor
        mutex_unlock(&mdev->state_lock);
 }
 
+static void mlx4_en_lock_and_stop_port(struct work_struct *work)
+{
+       struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv,
+                                                stop_port_task);
+       struct net_device *dev = priv->dev;
+       struct mlx4_en_dev *mdev = priv->mdev;
+ 
+       mutex_lock(&mdev->state_lock);
+       mlx4_en_do_stop_port(dev);
+       mutex_unlock(&mdev->state_lock);
+}
+
+static void mlx4_en_lock_and_start_port(struct work_struct *work)
+{
+       struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv,
+                                                start_port_task);
+       struct net_device *dev = priv->dev;
+       struct mlx4_en_dev *mdev = priv->mdev;
+
+       mutex_lock(&mdev->state_lock);
+       mlx4_en_do_start_port(dev);
+       mutex_unlock(&mdev->state_lock);
+}
 
-int mlx4_en_start_port(struct net_device *dev)
+int mlx4_en_do_start_port(struct net_device *dev)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
@@ -691,7 +728,7 @@ cq_err:
 }
 
 
-void mlx4_en_stop_port(struct net_device *dev)
+void mlx4_en_do_stop_port(struct net_device *dev)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
@@ -761,8 +798,8 @@ reset:
 
        mutex_lock(&mdev->state_lock);
        if (priv->port_up) {
-               mlx4_en_stop_port(dev);
-               if (mlx4_en_start_port(dev))
+               mlx4_en_do_stop_port(dev);
+               if (mlx4_en_do_start_port(dev))
                        en_err(priv, "Failed restarting port %d\n", priv->port);
        }
        mutex_unlock(&mdev->state_lock);
@@ -793,7 +830,7 @@ mlx4_en_init_locked(struct mlx4_en_priv 
        dev = priv->dev;
        mdev = priv->mdev;
        if (dev->if_drv_flags & IFF_DRV_RUNNING)
-               mlx4_en_stop_port(dev);
+               mlx4_en_do_stop_port(dev);
 
        if (!mdev->device_up) {
                en_err(priv, "Cannot open - device down/disabled\n");
@@ -816,7 +853,7 @@ mlx4_en_init_locked(struct mlx4_en_priv 
        }
 
        mlx4_en_set_default_moderation(priv);
-       if (mlx4_en_start_port(dev))
+       if (mlx4_en_do_start_port(dev))
                en_err(priv, "Failed starting port:%d\n", priv->port);
 }
 
@@ -905,7 +942,7 @@ void mlx4_en_destroy_netdev(struct net_d
                mlx4_free_hwq_res(mdev->dev, &priv->res, MLX4_EN_PAGE_SIZE);
 
        mutex_lock(&mdev->state_lock);
-       mlx4_en_stop_port(dev);
+       mlx4_en_do_stop_port(dev);
        mutex_unlock(&mdev->state_lock);
 
        cancel_delayed_work(&priv->stats_task);
@@ -925,7 +962,6 @@ void mlx4_en_destroy_netdev(struct net_d
 
        mtx_destroy(&priv->stats_lock.m);
        mtx_destroy(&priv->vlan_lock.m);
-       mtx_destroy(&priv->ioctl_lock.m);
        kfree(priv);
        if_free(dev);
 }
@@ -951,9 +987,9 @@ static int mlx4_en_change_mtu(struct net
                         * the port */
                        en_dbg(DRV, priv, "Change MTU called with card 
down!?\n");
                } else {
-                       mlx4_en_stop_port(dev);
+                       mlx4_en_do_stop_port(dev);
                        mlx4_en_set_default_moderation(priv);
-                       err = mlx4_en_start_port(dev);
+                       err = mlx4_en_do_start_port(dev);
                        if (err) {
                                en_err(priv, "Failed restarting port:%d\n",
                                         priv->port);
@@ -973,8 +1009,13 @@ static int mlx4_en_calc_media(struct mlx
        active = IFM_ETHER;
        if (priv->last_link_state == MLX4_DEV_EVENT_PORT_DOWN)
                return (active);
-       if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
-               return (active);
+       /*
+        * [ShaharK] mlx4_en_QUERY_PORT sleeps and cannot be called under a
+        * non-sleepable lock.
+        * I moved it to the periodic mlx4_en_do_get_stats.
+       if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
+               return (active);
+       */
        active |= IFM_FDX;
        trans_type = priv->port_state.transciver;
        /* XXX I don't know all of the transceiver values. */
@@ -1078,7 +1119,6 @@ static int mlx4_en_ioctl(struct ifnet *d
                error = -mlx4_en_change_mtu(dev, ifr->ifr_mtu);
                break;
        case SIOCSIFFLAGS:
-               mutex_lock(&mdev->state_lock);
                if (dev->if_flags & IFF_UP) {
                        if ((dev->if_drv_flags & IFF_DRV_RUNNING) == 0)
                                mlx4_en_start_port(dev);
@@ -1087,16 +1127,24 @@ static int mlx4_en_ioctl(struct ifnet *d
                } else {
                        if (dev->if_drv_flags & IFF_DRV_RUNNING) {
                                mlx4_en_stop_port(dev);
-                               if_link_state_change(dev, LINK_STATE_DOWN);
+                                if_link_state_change(dev, LINK_STATE_DOWN);
+                                /*
+                                * Since mlx4_en_stop_port is defered we
+                                * have to wait till it's finished.
+                                */
+                                for (int count=0; count<10; count++) {
+                                        if (dev->if_drv_flags & 
IFF_DRV_RUNNING) {
+                                                DELAY(20000);
+                                        } else {
+                                                break;
+                                        }
+                                }
                        }
                }
-               mutex_unlock(&mdev->state_lock);
                break;
        case SIOCADDMULTI:
        case SIOCDELMULTI:
-                spin_lock(&priv->ioctl_lock);
                mlx4_en_set_multicast(dev);
-                spin_unlock(&priv->ioctl_lock);
                break;
        case SIOCSIFMEDIA:
        case SIOCGIFMEDIA:
@@ -1153,7 +1201,7 @@ static int mlx4_en_set_ring_size(struct 
        mutex_lock(&mdev->state_lock);
        if (priv->port_up) {
                port_up = 1;
-               mlx4_en_stop_port(dev);
+               mlx4_en_do_stop_port(dev);
        }
        mlx4_en_free_resources(priv);
        priv->prof->tx_ring_size = tx_size;
@@ -1164,7 +1212,7 @@ static int mlx4_en_set_ring_size(struct 
                goto out;
        }
        if (port_up) {
-               err = mlx4_en_start_port(dev);
+               err = mlx4_en_do_start_port(dev);
                if (err)
                        en_err(priv, "Failed starting port\n");
        }
@@ -1256,7 +1304,7 @@ static int mlx4_en_set_rx_ppp(SYSCTL_HAN
                mutex_lock(&mdev->state_lock);
                if (priv->port_up) {
                        port_up = 1;
-                       mlx4_en_stop_port(priv->dev);
+                       mlx4_en_do_stop_port(priv->dev);
                }
                mlx4_en_free_resources(priv);
                priv->tx_ring_num = tx_ring_num;
@@ -1265,7 +1313,7 @@ static int mlx4_en_set_rx_ppp(SYSCTL_HAN
                if (error)
                        en_err(priv, "Failed reallocating port resources\n");
                if (error == 0 && port_up) {
-                       error = -mlx4_en_start_port(priv->dev);
+                       error = -mlx4_en_do_start_port(priv->dev);
                        if (error)
                                en_err(priv, "Failed starting port\n");
                }
@@ -1517,8 +1565,9 @@ int mlx4_en_init_netdev(struct mlx4_en_d
        priv->msg_enable = MLX4_EN_MSG_LEVEL;
        priv->ip_reasm = priv->mdev->profile.ip_reasm;
        mtx_init(&priv->stats_lock.m, "mlx4 stats", NULL, MTX_DEF);
-       mtx_init(&priv->ioctl_lock.m, "mlx4 ioctl", NULL, MTX_DEF);
        mtx_init(&priv->vlan_lock.m, "mlx4 vlan", NULL, MTX_DEF);
+       INIT_WORK(&priv->start_port_task, mlx4_en_lock_and_start_port);
+       INIT_WORK(&priv->stop_port_task, mlx4_en_lock_and_stop_port);
        INIT_WORK(&priv->mcast_task, mlx4_en_do_set_multicast);
        INIT_WORK(&priv->watchdog_task, mlx4_en_restart);
        INIT_WORK(&priv->linkstate_task, mlx4_en_linkstate);

Modified: head/sys/ofed/drivers/net/mlx4/mlx4_en.h
==============================================================================
--- head/sys/ofed/drivers/net/mlx4/mlx4_en.h    Sun Dec 15 07:04:59 2013        
(r259410)
+++ head/sys/ofed/drivers/net/mlx4/mlx4_en.h    Sun Dec 15 07:07:13 2013        
(r259411)
@@ -493,7 +493,6 @@ struct mlx4_en_priv {
        spinlock_t vlan_lock;
        struct mlx4_en_port_state port_state;
        spinlock_t stats_lock;
-       spinlock_t ioctl_lock;
 
        unsigned long last_moder_packets[MAX_RX_RINGS];
        unsigned long last_moder_tx_packets;
@@ -546,6 +545,8 @@ struct mlx4_en_priv {
        struct mlx4_en_cq rx_cq[MAX_RX_RINGS];
        struct mlx4_en_tx_hash_entry tx_hash[MLX4_EN_TX_HASH_SIZE];
        struct work_struct mcast_task;
+       struct work_struct start_port_task;
+       struct work_struct stop_port_task;
        struct work_struct watchdog_task;
        struct work_struct linkstate_task;
        struct delayed_work stats_task;
@@ -580,8 +581,8 @@ void mlx4_en_destroy_netdev(struct net_d
 int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
                        struct mlx4_en_port_profile *prof);
 
-int mlx4_en_start_port(struct net_device *dev);
-void mlx4_en_stop_port(struct net_device *dev);
+int mlx4_en_do_start_port(struct net_device *dev);
+void mlx4_en_do_stop_port(struct net_device *dev);
 
 void mlx4_en_free_resources(struct mlx4_en_priv *priv);
 int mlx4_en_alloc_resources(struct mlx4_en_priv *priv);
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to