struct mlx5e_profile {
        int     (*init)(struct mlx5_core_dev *mdev,
-                       struct net_device *netdev,
-                       const struct mlx5e_profile *profile, void *ppriv);
+                       struct net_device *netdev);
        void    (*cleanup)(struct mlx5e_priv *priv);
        int     (*init_rx)(struct mlx5e_priv *priv);
        void    (*cleanup_rx)(struct mlx5e_priv *priv);
                                 struct ethtool_pauseparam *pauseparam);
 
 /* mlx5e generic netdev management API */
+static inline unsigned int mlx5e_calc_max_nch(struct mlx5e_priv *priv)
+{
+       return priv->netdev->num_rx_queues / max_t(u8, priv->profile->rq_groups, 1);
+}
+
 int mlx5e_netdev_init(struct net_device *netdev,
                      struct mlx5e_priv *priv,
-                     struct mlx5_core_dev *mdev,
-                     const struct mlx5e_profile *profile,
-                     void *ppriv);
+                     struct mlx5_core_dev *mdev);
 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,
-                   int nch, void *ppriv);
+struct net_device *
+mlx5e_create_netdev(struct mlx5_core_dev *mdev, unsigned int txqs, unsigned int rxqs);
 int mlx5e_attach_netdev(struct mlx5e_priv *priv);
 void mlx5e_detach_netdev(struct mlx5e_priv *priv);
 void mlx5e_destroy_netdev(struct mlx5e_priv *priv);
 void mlx5e_set_netdev_mtu_boundaries(struct mlx5e_priv *priv);
-void mlx5e_build_nic_params(struct mlx5e_priv *priv,
-                           struct mlx5e_xsk *xsk,
-                           struct mlx5e_rss_params *rss_params,
-                           struct mlx5e_params *params,
-                           u16 mtu);
+void mlx5e_build_nic_params(struct mlx5e_priv *priv, struct mlx5e_xsk *xsk, u16 mtu);
 void mlx5e_build_rq_params(struct mlx5_core_dev *mdev,
                           struct mlx5e_params *params);
 void mlx5e_build_rss_params(struct mlx5e_rss_params *rss_params,
 
                if (!params->vlan_strip_disable)
                        netdev_warn(netdev, "Dropping C-tag vlan stripping offload due to S-tag vlan\n");
        }
+
        if (!MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_STRIDING_RQ)) {
                if (features & NETIF_F_LRO) {
                        netdev_warn(netdev, "Disabling LRO, not supported in legacy RQ\n");
                        tirc_default_config[tt].rx_hash_fields;
 }
 
-void mlx5e_build_nic_params(struct mlx5e_priv *priv,
-                           struct mlx5e_xsk *xsk,
-                           struct mlx5e_rss_params *rss_params,
-                           struct mlx5e_params *params,
-                           u16 mtu)
+void mlx5e_build_nic_params(struct mlx5e_priv *priv, struct mlx5e_xsk *xsk, u16 mtu)
 {
+       struct mlx5e_rss_params *rss_params = &priv->rss_params;
+       struct mlx5e_params *params = &priv->channels.params;
        struct mlx5_core_dev *mdev = priv->mdev;
        u8 rx_cq_period_mode;
 
+       priv->max_nch = mlx5e_calc_max_nch(priv);
+
        params->sw_mtu = mtu;
        params->hard_mtu = MLX5E_ETH_HARD_MTU;
        params->num_channels = min_t(unsigned int, MLX5E_MAX_NUM_CHANNELS / 2,
 
        /* AF_XDP */
        params->xsk = xsk;
+
+       /* Do not update netdev->features directly in here
+        * on mlx5e_attach_netdev() we will call mlx5e_update_features()
+        * To update netdev->features please modify mlx5e_fix_features()
+        */
 }
 
 static void mlx5e_set_netdev_dev_addr(struct net_device *netdev)
                netdev->hw_features |= NETIF_F_RXFCS;
 
        netdev->features          = netdev->hw_features;
-       if (!priv->channels.params.lro_en)
-               netdev->features  &= ~NETIF_F_LRO;
 
+       /* Defaults */
        if (fcs_enabled)
                netdev->features  &= ~NETIF_F_RXALL;
-
-       if (!priv->channels.params.scatter_fcs_en)
-               netdev->features  &= ~NETIF_F_RXFCS;
-
-       /* prefere CQE compression over rxhash */
-       if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_RX_CQE_COMPRESS))
-               netdev->features &= ~NETIF_F_RXHASH;
+       netdev->features  &= ~NETIF_F_LRO;
+       netdev->features  &= ~NETIF_F_RXFCS;
 
 #define FT_CAP(f) MLX5_CAP_FLOWTABLE(mdev, flow_table_properties_nic_receive.f)
        if (FT_CAP(flow_modify_en) &&
 }
 
 static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
-                         struct net_device *netdev,
-                         const struct mlx5e_profile *profile,
-                         void *ppriv)
+                         struct net_device *netdev)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
-       struct mlx5e_rss_params *rss = &priv->rss_params;
        int err;
 
-       err = mlx5e_netdev_init(netdev, priv, mdev, profile, ppriv);
-       if (err)
-               return err;
-
-       mlx5e_build_nic_params(priv, &priv->xsk, rss, &priv->channels.params,
-                              netdev->mtu);
+       mlx5e_build_nic_params(priv, &priv->xsk, netdev->mtu);
 
        mlx5e_timestamp_init(priv);
 
        err = mlx5e_ipsec_init(priv);
        if (err)
                mlx5_core_err(mdev, "IPSec initialization failed, %d\n", err);
+
        err = mlx5e_tls_init(priv);
        if (err)
                mlx5_core_err(mdev, "TLS initialization failed, %d\n", err);
-       mlx5e_build_nic_netdev(netdev);
+
        err = mlx5e_devlink_port_register(priv);
        if (err)
                mlx5_core_err(mdev, "mlx5e_devlink_port_register failed, %d\n", err);
+
        mlx5e_health_create_reporters(priv);
 
        return 0;
        mlx5e_devlink_port_unregister(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,
-                     struct mlx5_core_dev *mdev,
-                     const struct mlx5e_profile *profile,
-                     void *ppriv)
+                     struct mlx5_core_dev *mdev)
 {
        /* priv init */
        priv->mdev        = mdev;
        priv->netdev      = netdev;
-       priv->profile     = profile;
-       priv->ppriv       = ppriv;
        priv->msglevel    = MLX5E_MSG_LEVEL;
-       priv->max_nch     = netdev->num_rx_queues / max_t(u8, profile->rq_groups, 1);
        priv->max_opened_tc = 1;
 
        if (!alloc_cpumask_var(&priv->scratchpad.cpumask, GFP_KERNEL))
        kvfree(priv->htb.qos_sq_stats);
 }
 
-struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev,
-                                      const struct mlx5e_profile *profile,
-                                      int nch,
-                                      void *ppriv)
+struct net_device *
+mlx5e_create_netdev(struct mlx5_core_dev *mdev, unsigned int txqs, unsigned int rxqs)
 {
        struct net_device *netdev;
-       unsigned int ptp_txqs = 0;
-       int qos_sqs = 0;
        int err;
 
-       if (MLX5_CAP_GEN(mdev, ts_cqe_to_dest_cqn))
-               ptp_txqs = profile->max_tc;
-
-       if (mlx5_qos_is_supported(mdev))
-               qos_sqs = mlx5e_qos_max_leaf_nodes(mdev);
-
-       netdev = alloc_etherdev_mqs(sizeof(struct mlx5e_priv),
-                                   nch * profile->max_tc + ptp_txqs + qos_sqs,
-                                   nch * profile->rq_groups);
+       netdev = alloc_etherdev_mqs(sizeof(struct mlx5e_priv), txqs, rxqs);
        if (!netdev) {
                mlx5_core_err(mdev, "alloc_etherdev_mqs() failed\n");
                return NULL;
        }
 
-       err = profile->init(mdev, netdev, profile, ppriv);
+       err = mlx5e_netdev_init(netdev, netdev_priv(netdev), mdev);
        if (err) {
-               mlx5_core_err(mdev, "failed to init mlx5e profile %d\n", err);
+               mlx5_core_err(mdev, "mlx5e_netdev_init failed, err=%d\n", err);
                goto err_free_netdev;
        }
+       dev_net_set(netdev, mlx5_core_net(mdev));
 
        return netdev;
 
        return NULL;
 }
 
+static void mlx5e_update_features(struct net_device *netdev)
+{
+       if (netdev->reg_state != NETREG_REGISTERED)
+               return; /* features will be updated on netdev registration */
+
+       rtnl_lock();
+       netdev_update_features(netdev);
+       rtnl_unlock();
+}
+
 int mlx5e_attach_netdev(struct mlx5e_priv *priv)
 {
        const bool take_rtnl = priv->netdev->reg_state == NETREG_REGISTERED;
-       const struct mlx5e_profile *profile;
+       const struct mlx5e_profile *profile = priv->profile;
        int max_nch;
        int err;
 
-       profile = priv->profile;
        clear_bit(MLX5E_STATE_DESTROYING, &priv->state);
 
        /* max number of channels may have changed */
        if (profile->enable)
                profile->enable(priv);
 
+       mlx5e_update_features(priv->netdev);
+
        return 0;
 
 err_cleanup_tx:
 
 void mlx5e_destroy_netdev(struct mlx5e_priv *priv)
 {
-       const struct mlx5e_profile *profile = priv->profile;
        struct net_device *netdev = priv->netdev;
 
-       if (profile->cleanup)
-               profile->cleanup(priv);
+       mlx5e_netdev_cleanup(netdev, priv);
        free_netdev(netdev);
 }
 
                       const struct auxiliary_device_id *id)
 {
        struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
+       const struct mlx5e_profile *profile = &mlx5e_nic_profile;
        struct mlx5_core_dev *mdev = edev->mdev;
        struct net_device *netdev;
        pm_message_t state = {};
-       void *priv;
+       unsigned int txqs, rxqs, ptp_txqs = 0;
+       struct mlx5e_priv *priv;
+       int qos_sqs = 0;
        int err;
        int nch;
 
+       if (MLX5_CAP_GEN(mdev, ts_cqe_to_dest_cqn))
+               ptp_txqs = profile->max_tc;
+
+       if (mlx5_qos_is_supported(mdev))
+               qos_sqs = mlx5e_qos_max_leaf_nodes(mdev);
+
        nch = mlx5e_get_max_num_channels(mdev);
-       netdev = mlx5e_create_netdev(mdev, &mlx5e_nic_profile, nch, NULL);
+       txqs = nch * profile->max_tc + ptp_txqs + qos_sqs;
+       rxqs = nch * profile->rq_groups;
+       netdev = mlx5e_create_netdev(mdev, txqs, rxqs);
        if (!netdev) {
                mlx5_core_err(mdev, "mlx5e_create_netdev failed\n");
                return -ENOMEM;
        }
 
-       dev_net_set(netdev, mlx5_core_net(mdev));
+       mlx5e_build_nic_netdev(netdev);
+
        priv = netdev_priv(netdev);
        dev_set_drvdata(&adev->dev, priv);
 
+       priv->profile = profile;
+       priv->ppriv = NULL;
+       err = profile->init(mdev, netdev);
+       if (err) {
+               mlx5_core_err(mdev, "mlx5e_nic_profile init failed, %d\n", err);
+               goto err_destroy_netdev;
+       }
+
        err = mlx5e_resume(adev);
        if (err) {
                mlx5_core_err(mdev, "mlx5e_resume failed, %d\n", err);
-               goto err_destroy_netdev;
+               goto err_profile_cleanup;
        }
 
        err = register_netdev(netdev);
 
 err_resume:
        mlx5e_suspend(adev, state);
+err_profile_cleanup:
+       profile->cleanup(priv);
 err_destroy_netdev:
        mlx5e_destroy_netdev(priv);
        return err;
        mlx5e_dcbnl_delete_app(priv);
        unregister_netdev(priv->netdev);
        mlx5e_suspend(adev, state);
+       priv->profile->cleanup(priv);
        mlx5e_destroy_netdev(priv);
 }
 
 
                                         MLX5_CQ_PERIOD_MODE_START_FROM_CQE :
                                         MLX5_CQ_PERIOD_MODE_START_FROM_EQE;
 
+       priv->max_nch = mlx5e_calc_max_nch(priv);
        params = &priv->channels.params;
+
+       params->num_channels = MLX5E_REP_PARAMS_DEF_NUM_CHANNELS;
        params->hard_mtu    = MLX5E_ETH_HARD_MTU;
        params->sw_mtu      = netdev->mtu;
 
        mlx5e_build_rss_params(&priv->rss_params, params->num_channels);
 }
 
-static void mlx5e_build_rep_netdev(struct net_device *netdev)
+static void mlx5e_build_rep_netdev(struct net_device *netdev,
+                                  struct mlx5_core_dev *mdev,
+                                  struct mlx5_eswitch_rep *rep)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
-       struct mlx5e_rep_priv *rpriv = priv->ppriv;
-       struct mlx5_eswitch_rep *rep = rpriv->rep;
-       struct mlx5_core_dev *mdev = priv->mdev;
 
        SET_NETDEV_DEV(netdev, mdev->device);
        if (rep->vport == MLX5_VPORT_UPLINK) {
 }
 
 static int mlx5e_init_rep(struct mlx5_core_dev *mdev,
-                         struct net_device *netdev,
-                         const struct mlx5e_profile *profile,
-                         void *ppriv)
+                         struct net_device *netdev)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
-       int err;
-
-       err = mlx5e_netdev_init(netdev, priv, mdev, profile, ppriv);
-       if (err)
-               return err;
-
-       priv->channels.params.num_channels = MLX5E_REP_PARAMS_DEF_NUM_CHANNELS;
 
        mlx5e_build_rep_params(netdev);
-       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)
        struct mlx5e_rep_priv *rpriv;
        struct devlink_port *dl_port;
        struct net_device *netdev;
+       struct mlx5e_priv *priv;
+       unsigned int txqs, rxqs;
        int nch, err;
 
        rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL);
        /* rpriv->rep to be looked up when profile->init() is called */
        rpriv->rep = rep;
 
-       nch = mlx5e_get_max_num_channels(dev);
        profile = (rep->vport == MLX5_VPORT_UPLINK) ?
                  &mlx5e_uplink_rep_profile : &mlx5e_rep_profile;
-       netdev = mlx5e_create_netdev(dev, profile, nch, rpriv);
+
+       nch = mlx5e_get_max_num_channels(dev);
+       txqs = nch * profile->max_tc;
+       rxqs = nch * profile->rq_groups;
+       netdev = mlx5e_create_netdev(dev, txqs, rxqs);
        if (!netdev) {
                mlx5_core_warn(dev,
                               "Failed to create representor netdev for vport %d\n",
                return -EINVAL;
        }
 
-       dev_net_set(netdev, mlx5_core_net(dev));
+       mlx5e_build_rep_netdev(netdev, dev, rep);
+
        rpriv->netdev = netdev;
        rep->rep_data[REP_ETH].priv = rpriv;
        INIT_LIST_HEAD(&rpriv->vport_sqs_list);
                        goto err_destroy_netdev;
        }
 
+       priv = netdev_priv(netdev);
+       priv->profile = profile;
+       priv->ppriv = rpriv;
+       err = profile->init(dev, netdev);
+       if (err) {
+               netdev_warn(netdev, "rep profile init failed, %d\n", err);
+               goto err_destroy_mdev_resources;
+       }
+
        err = mlx5e_attach_netdev(netdev_priv(netdev));
        if (err) {
                netdev_warn(netdev,
                            "Failed to attach representor netdev for vport %d\n",
                            rep->vport);
-               goto err_destroy_mdev_resources;
+               goto err_cleanup_profile;
        }
 
        err = mlx5e_rep_neigh_init(rpriv);
 err_detach_netdev:
        mlx5e_detach_netdev(netdev_priv(netdev));
 
+err_cleanup_profile:
+       priv->profile->cleanup(priv);
+
 err_destroy_mdev_resources:
        if (rep->vport == MLX5_VPORT_UPLINK)
                mlx5e_destroy_mdev_resources(dev);
        unregister_netdev(netdev);
        mlx5e_rep_neigh_cleanup(rpriv);
        mlx5e_detach_netdev(priv);
+       priv->profile->cleanup(priv);
        if (rep->vport == MLX5_VPORT_UPLINK)
                mlx5e_destroy_mdev_resources(priv->mdev);
        mlx5e_destroy_netdev(priv);