};
 
 struct mlx5e_profile {
-       void    (*init)(struct mlx5_core_dev *mdev,
+       int     (*init)(struct mlx5_core_dev *mdev,
                        struct net_device *netdev,
                        const struct mlx5e_profile *profile, void *ppriv);
        void    (*cleanup)(struct mlx5e_priv *priv);
                               struct ethtool_flash *flash);
 
 /* mlx5e generic netdev management API */
+int mlx5e_netdev_init(struct net_device *netdev, struct mlx5e_priv *priv);
+void mlx5e_netdev_cleanup(struct net_device *netdev, struct mlx5e_priv *priv);
 struct net_device*
 mlx5e_create_netdev(struct mlx5_core_dev *mdev, const struct mlx5e_profile *profile,
                    void *ppriv);
 
                mlx5_core_dealloc_q_counter(priv->mdev, priv->drop_rq_q_counter);
 }
 
-static void mlx5e_nic_init(struct mlx5_core_dev *mdev,
-                          struct net_device *netdev,
-                          const struct mlx5e_profile *profile,
-                          void *ppriv)
+static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
+                         struct net_device *netdev,
+                         const struct mlx5e_profile *profile,
+                         void *ppriv)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
        int err;
 
+       err = mlx5e_netdev_init(netdev, priv);
+       if (err)
+               return err;
+
        mlx5e_build_nic_netdev_priv(mdev, netdev, profile, ppriv);
        err = mlx5e_ipsec_init(priv);
        if (err)
                mlx5_core_err(mdev, "TLS initialization failed, %d\n", err);
        mlx5e_build_nic_netdev(netdev);
        mlx5e_build_tc2txq_maps(priv);
+
+       return 0;
 }
 
 static void mlx5e_nic_cleanup(struct mlx5e_priv *priv)
 {
        mlx5e_tls_cleanup(priv);
        mlx5e_ipsec_cleanup(priv);
+       mlx5e_netdev_cleanup(priv->netdev, priv);
 }
 
 static int mlx5e_init_nic_rx(struct mlx5e_priv *priv)
 
 /* mlx5e generic netdev management API (move to en_common.c) */
 
+/* mlx5e_netdev_init/cleanup must be called from profile->init/cleanup callbacks */
+int mlx5e_netdev_init(struct net_device *netdev, struct mlx5e_priv *priv)
+{
+       netif_carrier_off(netdev);
+
+       priv->wq = create_singlethread_workqueue("mlx5e");
+       if (!priv->wq)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void mlx5e_netdev_cleanup(struct net_device *netdev, struct mlx5e_priv *priv)
+{
+       destroy_workqueue(priv->wq);
+}
+
 struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev,
                                       const struct mlx5e_profile *profile,
                                       void *ppriv)
 {
        int nch = profile->max_nch(mdev);
        struct net_device *netdev;
-       struct mlx5e_priv *priv;
+       int err;
 
        netdev = alloc_etherdev_mqs(sizeof(struct mlx5e_priv),
                                    nch * profile->max_tc,
        netdev->rx_cpu_rmap = mdev->rmap;
 #endif
 
-       profile->init(mdev, netdev, profile, ppriv);
-
-       netif_carrier_off(netdev);
-
-       priv = netdev_priv(netdev);
-
-       priv->wq = create_singlethread_workqueue("mlx5e");
-       if (!priv->wq)
-               goto err_cleanup_nic;
+       err = profile->init(mdev, netdev, profile, ppriv);
+       if (err) {
+               mlx5_core_err(mdev, "failed to init mlx5e profile %d\n", err);
+               goto err_free_netdev;
+       }
 
        return netdev;
 
-err_cleanup_nic:
-       if (profile->cleanup)
-               profile->cleanup(priv);
+err_free_netdev:
        free_netdev(netdev);
 
        return NULL;
        const struct mlx5e_profile *profile = priv->profile;
        struct net_device *netdev = priv->netdev;
 
-       destroy_workqueue(priv->wq);
        if (profile->cleanup)
                profile->cleanup(priv);
        free_netdev(netdev);
 
        netdev->max_mtu = MLX5E_HW2SW_MTU(&priv->channels.params, max_mtu);
 }
 
-static void mlx5e_init_rep(struct mlx5_core_dev *mdev,
-                          struct net_device *netdev,
-                          const struct mlx5e_profile *profile,
-                          void *ppriv)
+static int mlx5e_init_rep(struct mlx5_core_dev *mdev,
+                         struct net_device *netdev,
+                         const struct mlx5e_profile *profile,
+                         void *ppriv)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
+       int err;
 
        priv->mdev                         = mdev;
        priv->netdev                       = netdev;
        priv->profile                      = profile;
        priv->ppriv                        = ppriv;
 
-       mutex_init(&priv->state_lock);
+       err = mlx5e_netdev_init(netdev, priv);
+       if (err)
+               return err;
 
        INIT_DELAYED_WORK(&priv->update_stats_work, mlx5e_update_stats_work);
 
-       priv->channels.params.num_channels = 1;
+       mutex_init(&priv->state_lock);
+
+       priv->channels.params.num_channels = profile->max_nch(mdev);
 
        mlx5e_build_rep_params(mdev, &priv->channels.params, netdev->mtu);
        mlx5e_build_rep_netdev(netdev);
 
        mlx5e_timestamp_init(priv);
+
+       return 0;
+}
+
+static void mlx5e_cleanup_rep(struct mlx5e_priv *priv)
+{
+       mlx5e_netdev_cleanup(priv->netdev, priv);
 }
 
 static int mlx5e_create_rep_ttc_table(struct mlx5e_priv *priv)
 
 static const struct mlx5e_profile mlx5e_rep_profile = {
        .init                   = mlx5e_init_rep,
+       .cleanup                = mlx5e_cleanup_rep,
        .init_rx                = mlx5e_init_rep_rx,
        .cleanup_rx             = mlx5e_cleanup_rep_rx,
        .init_tx                = mlx5e_init_rep_tx,
 
 }
 
 /* Called directly after IPoIB netdevice was created to initialize SW structs */
-void mlx5i_init(struct mlx5_core_dev *mdev,
-               struct net_device *netdev,
-               const struct mlx5e_profile *profile,
-               void *ppriv)
+int mlx5i_init(struct mlx5_core_dev *mdev,
+              struct net_device *netdev,
+              const struct mlx5e_profile *profile,
+              void *ppriv)
 {
        struct mlx5e_priv *priv  = mlx5i_epriv(netdev);
        u16 max_mtu;
+       int err;
 
        /* priv init */
        priv->mdev        = mdev;
        priv->max_opened_tc = 1;
        mutex_init(&priv->state_lock);
 
+       err = mlx5e_netdev_init(netdev, priv);
+       if (err)
+               return err;
+
        mlx5_query_port_max_mtu(mdev, &max_mtu, 1);
        netdev->mtu = max_mtu;
 
 
        netdev->netdev_ops = &mlx5i_netdev_ops;
        netdev->ethtool_ops = &mlx5i_ethtool_ops;
+
+       return 0;
 }
 
 /* Called directly before IPoIB netdevice is destroyed to cleanup SW structs */
-static void mlx5i_cleanup(struct mlx5e_priv *priv)
+void mlx5i_cleanup(struct mlx5e_priv *priv)
 {
-       /* Do nothing .. */
+       mlx5e_netdev_cleanup(priv->netdev, priv);
 }
 
 static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv)
 
        mlx5e_detach_netdev(priv);
        profile->cleanup(priv);
-       destroy_workqueue(priv->wq);
 
        if (!ipriv->sub_interface) {
                mlx5i_pkey_qpn_ht_cleanup(netdev);
        ipriv = netdev_priv(netdev);
        epriv = mlx5i_epriv(netdev);
 
-       epriv->wq = create_singlethread_workqueue("mlx5i");
-       if (!epriv->wq)
-               return -ENOMEM;
-
        ipriv->sub_interface = mlx5_is_sub_interface(mdev);
        if (!ipriv->sub_interface) {
                err = mlx5i_pkey_qpn_ht_init(netdev);
                if (err) {
                        mlx5_core_warn(mdev, "allocate qpn_to_netdev ht failed\n");
-                       goto destroy_wq;
+                       return err;
                }
 
                /* This should only be called once per mdev */
 
 destroy_ht:
        mlx5i_pkey_qpn_ht_cleanup(netdev);
-destroy_wq:
-       destroy_workqueue(epriv->wq);
        return err;
 }
 
 
 int mlx5i_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
 
 /* Parent profile functions */
-void mlx5i_init(struct mlx5_core_dev *mdev,
-               struct net_device *netdev,
-               const struct mlx5e_profile *profile,
-               void *ppriv);
+int mlx5i_init(struct mlx5_core_dev *mdev,
+              struct net_device *netdev,
+              const struct mlx5e_profile *profile,
+              void *ppriv);
+void mlx5i_cleanup(struct mlx5e_priv *priv);
 
 /* Get child interface nic profile */
 const struct mlx5e_profile *mlx5i_pkey_get_profile(void);
 
 }
 
 /* Called directly after IPoIB netdevice was created to initialize SW structs */
-static void mlx5i_pkey_init(struct mlx5_core_dev *mdev,
-                            struct net_device *netdev,
-                            const struct mlx5e_profile *profile,
-                            void *ppriv)
+static int mlx5i_pkey_init(struct mlx5_core_dev *mdev,
+                          struct net_device *netdev,
+                          const struct mlx5e_profile *profile,
+                          void *ppriv)
 {
        struct mlx5e_priv *priv  = mlx5i_epriv(netdev);
+       int err;
 
-       mlx5i_init(mdev, netdev, profile, ppriv);
+       err = mlx5i_init(mdev, netdev, profile, ppriv);
+       if (err)
+               return err;
 
        /* Override parent ndo */
        netdev->netdev_ops = &mlx5i_pkey_netdev_ops;
 
        /* Use dummy rqs */
        priv->channels.params.log_rq_mtu_frames = MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE;
+
+       return 0;
 }
 
 /* Called directly before IPoIB netdevice is destroyed to cleanup SW structs */
 static void mlx5i_pkey_cleanup(struct mlx5e_priv *priv)
 {
-       /* Do nothing .. */
+       mlx5i_cleanup(priv);
 }
 
 static int mlx5i_pkey_init_tx(struct mlx5e_priv *priv)