net/mlx5e: Add support to neighbour update flow
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / en_rep.c
index 53db5ec2c1225a7960e78965403aced80eecc94d..730de6b7e46e61fac786e2cb945ce2ad6e332d16 100644 (file)
 #include <linux/mlx5/fs.h>
 #include <net/switchdev.h>
 #include <net/pkt_cls.h>
+#include <net/netevent.h>
+#include <net/arp.h>
 
 #include "eswitch.h"
 #include "en.h"
+#include "en_rep.h"
 #include "en_tc.h"
 
 static const char mlx5e_rep_driver_name[] = "mlx5e_rep";
@@ -75,7 +78,8 @@ static void mlx5e_rep_get_strings(struct net_device *dev,
 static void mlx5e_rep_update_hw_counters(struct mlx5e_priv *priv)
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
-       struct mlx5_eswitch_rep *rep = priv->ppriv;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
        struct rtnl_link_stats64 *vport_stats;
        struct ifla_vf_stats vf_stats;
        int err;
@@ -165,7 +169,8 @@ static const struct ethtool_ops mlx5e_rep_ethtool_ops = {
 int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
-       struct mlx5_eswitch_rep *rep = priv->ppriv;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
 
        if (esw->mode == SRIOV_NONE)
@@ -184,10 +189,10 @@ int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr)
 }
 
 int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv)
-
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
-       struct mlx5_eswitch_rep *rep = priv->ppriv;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
        struct mlx5e_channel *c;
        int n, tc, num_sqs = 0;
        int err = -ENOMEM;
@@ -212,42 +217,308 @@ out:
        return err;
 }
 
-int mlx5e_nic_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep)
+void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv)
 {
-       struct net_device *netdev = rep->netdev;
+       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
+
+       mlx5_eswitch_sqs2vport_stop(esw, rep);
+}
+
+static void mlx5e_rep_neigh_entry_hold(struct mlx5e_neigh_hash_entry *nhe)
+{
+       refcount_inc(&nhe->refcnt);
+}
+
+static void mlx5e_rep_neigh_entry_release(struct mlx5e_neigh_hash_entry *nhe)
+{
+       if (refcount_dec_and_test(&nhe->refcnt))
+               kfree(nhe);
+}
+
+static void mlx5e_rep_update_flows(struct mlx5e_priv *priv,
+                                  struct mlx5e_encap_entry *e,
+                                  bool neigh_connected,
+                                  unsigned char ha[ETH_ALEN])
+{
+       struct ethhdr *eth = (struct ethhdr *)e->encap_header;
+
+       ASSERT_RTNL();
+
+       if ((!neigh_connected && (e->flags & MLX5_ENCAP_ENTRY_VALID)) ||
+           !ether_addr_equal(e->h_dest, ha))
+               mlx5e_tc_encap_flows_del(priv, e);
+
+       if (neigh_connected && !(e->flags & MLX5_ENCAP_ENTRY_VALID)) {
+               ether_addr_copy(e->h_dest, ha);
+               ether_addr_copy(eth->h_dest, ha);
+
+               mlx5e_tc_encap_flows_add(priv, e);
+       }
+}
+
+static void mlx5e_rep_neigh_update(struct work_struct *work)
+{
+       struct mlx5e_neigh_hash_entry *nhe =
+               container_of(work, struct mlx5e_neigh_hash_entry, neigh_update_work);
+       struct neighbour *n = nhe->n;
+       struct mlx5e_encap_entry *e;
+       unsigned char ha[ETH_ALEN];
+       struct mlx5e_priv *priv;
+       bool neigh_connected;
+       bool encap_connected;
+       u8 nud_state, dead;
+
+       rtnl_lock();
+
+       /* If these parameters are changed after we release the lock,
+        * we'll receive another event letting us know about it.
+        * We use this lock to avoid inconsistency between the neigh validity
+        * and it's hw address.
+        */
+       read_lock_bh(&n->lock);
+       memcpy(ha, n->ha, ETH_ALEN);
+       nud_state = n->nud_state;
+       dead = n->dead;
+       read_unlock_bh(&n->lock);
+
+       neigh_connected = (nud_state & NUD_VALID) && !dead;
+
+       list_for_each_entry(e, &nhe->encap_list, encap_list) {
+               encap_connected = !!(e->flags & MLX5_ENCAP_ENTRY_VALID);
+               priv = netdev_priv(e->out_dev);
+
+               if (encap_connected != neigh_connected ||
+                   !ether_addr_equal(e->h_dest, ha))
+                       mlx5e_rep_update_flows(priv, e, neigh_connected, ha);
+       }
+       mlx5e_rep_neigh_entry_release(nhe);
+       rtnl_unlock();
+       neigh_release(n);
+}
+
+static struct mlx5e_neigh_hash_entry *
+mlx5e_rep_neigh_entry_lookup(struct mlx5e_priv *priv,
+                            struct mlx5e_neigh *m_neigh);
+
+static int mlx5e_rep_netevent_event(struct notifier_block *nb,
+                                   unsigned long event, void *ptr)
+{
+       struct mlx5e_rep_priv *rpriv = container_of(nb, struct mlx5e_rep_priv,
+                                                   neigh_update.netevent_nb);
+       struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update;
+       struct net_device *netdev = rpriv->rep->netdev;
        struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_neigh_hash_entry *nhe = NULL;
+       struct mlx5e_neigh m_neigh = {};
+       struct neighbour *n;
+
+       switch (event) {
+       case NETEVENT_NEIGH_UPDATE:
+               n = ptr;
+#if IS_ENABLED(CONFIG_IPV6)
+               if (n->tbl != ipv6_stub->nd_tbl && n->tbl != &arp_tbl)
+#else
+               if (n->tbl != &arp_tbl)
+#endif
+                       return NOTIFY_DONE;
+
+               m_neigh.dev = n->dev;
+               memcpy(&m_neigh.dst_ip, n->primary_key, n->tbl->key_len);
+
+               /* We are in atomic context and can't take RTNL mutex, so use
+                * spin_lock_bh to lookup the neigh table. bh is used since
+                * netevent can be called from a softirq context.
+                */
+               spin_lock_bh(&neigh_update->encap_lock);
+               nhe = mlx5e_rep_neigh_entry_lookup(priv, &m_neigh);
+               if (!nhe) {
+                       spin_unlock_bh(&neigh_update->encap_lock);
+                       return NOTIFY_DONE;
+               }
 
-       if (test_bit(MLX5E_STATE_OPENED, &priv->state))
-               return mlx5e_add_sqs_fwd_rules(priv);
+               /* This assignment is valid as long as the the neigh reference
+                * is taken
+                */
+               nhe->n = n;
+
+               /* Take a reference to ensure the neighbour and mlx5 encap
+                * entry won't be destructed until we drop the reference in
+                * delayed work.
+                */
+               neigh_hold(n);
+               mlx5e_rep_neigh_entry_hold(nhe);
+
+               if (!queue_work(priv->wq, &nhe->neigh_update_work)) {
+                       mlx5e_rep_neigh_entry_release(nhe);
+                       neigh_release(n);
+               }
+               spin_unlock_bh(&neigh_update->encap_lock);
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+static const struct rhashtable_params mlx5e_neigh_ht_params = {
+       .head_offset = offsetof(struct mlx5e_neigh_hash_entry, rhash_node),
+       .key_offset = offsetof(struct mlx5e_neigh_hash_entry, m_neigh),
+       .key_len = sizeof(struct mlx5e_neigh),
+       .automatic_shrinking = true,
+};
+
+static int mlx5e_rep_neigh_init(struct mlx5e_rep_priv *rpriv)
+{
+       struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update;
+       int err;
+
+       err = rhashtable_init(&neigh_update->neigh_ht, &mlx5e_neigh_ht_params);
+       if (err)
+               return err;
+
+       INIT_LIST_HEAD(&neigh_update->neigh_list);
+       spin_lock_init(&neigh_update->encap_lock);
+
+       rpriv->neigh_update.netevent_nb.notifier_call = mlx5e_rep_netevent_event;
+       err = register_netevent_notifier(&rpriv->neigh_update.netevent_nb);
+       if (err)
+               goto out_err;
        return 0;
+
+out_err:
+       rhashtable_destroy(&neigh_update->neigh_ht);
+       return err;
 }
 
-void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv)
+static void mlx5e_rep_neigh_cleanup(struct mlx5e_rep_priv *rpriv)
 {
-       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
-       struct mlx5_eswitch_rep *rep = priv->ppriv;
+       struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update;
+       struct mlx5e_priv *priv = netdev_priv(rpriv->rep->netdev);
 
-       mlx5_eswitch_sqs2vport_stop(esw, rep);
+       unregister_netevent_notifier(&neigh_update->netevent_nb);
+
+       flush_workqueue(priv->wq); /* flush neigh update works */
+
+       rhashtable_destroy(&neigh_update->neigh_ht);
 }
 
-void mlx5e_nic_rep_unload(struct mlx5_eswitch *esw,
-                         struct mlx5_eswitch_rep *rep)
+static int mlx5e_rep_neigh_entry_insert(struct mlx5e_priv *priv,
+                                       struct mlx5e_neigh_hash_entry *nhe)
 {
-       struct net_device *netdev = rep->netdev;
-       struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       int err;
 
-       if (test_bit(MLX5E_STATE_OPENED, &priv->state))
-               mlx5e_remove_sqs_fwd_rules(priv);
+       err = rhashtable_insert_fast(&rpriv->neigh_update.neigh_ht,
+                                    &nhe->rhash_node,
+                                    mlx5e_neigh_ht_params);
+       if (err)
+               return err;
 
-       /* clean (and re-init) existing uplink offloaded TC rules */
-       mlx5e_tc_cleanup(priv);
-       mlx5e_tc_init(priv);
+       list_add(&nhe->neigh_list, &rpriv->neigh_update.neigh_list);
+
+       return err;
+}
+
+static void mlx5e_rep_neigh_entry_remove(struct mlx5e_priv *priv,
+                                        struct mlx5e_neigh_hash_entry *nhe)
+{
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+
+       spin_lock_bh(&rpriv->neigh_update.encap_lock);
+
+       list_del(&nhe->neigh_list);
+
+       rhashtable_remove_fast(&rpriv->neigh_update.neigh_ht,
+                              &nhe->rhash_node,
+                              mlx5e_neigh_ht_params);
+       spin_unlock_bh(&rpriv->neigh_update.encap_lock);
+}
+
+/* This function must only be called under RTNL lock or under the
+ * representor's encap_lock in case RTNL mutex can't be held.
+ */
+static struct mlx5e_neigh_hash_entry *
+mlx5e_rep_neigh_entry_lookup(struct mlx5e_priv *priv,
+                            struct mlx5e_neigh *m_neigh)
+{
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update;
+
+       return rhashtable_lookup_fast(&neigh_update->neigh_ht, m_neigh,
+                                     mlx5e_neigh_ht_params);
+}
+
+static int mlx5e_rep_neigh_entry_create(struct mlx5e_priv *priv,
+                                       struct mlx5e_encap_entry *e,
+                                       struct mlx5e_neigh_hash_entry **nhe)
+{
+       int err;
+
+       *nhe = kzalloc(sizeof(**nhe), GFP_KERNEL);
+       if (!*nhe)
+               return -ENOMEM;
+
+       memcpy(&(*nhe)->m_neigh, &e->m_neigh, sizeof(e->m_neigh));
+       INIT_WORK(&(*nhe)->neigh_update_work, mlx5e_rep_neigh_update);
+       INIT_LIST_HEAD(&(*nhe)->encap_list);
+       refcount_set(&(*nhe)->refcnt, 1);
+
+       err = mlx5e_rep_neigh_entry_insert(priv, *nhe);
+       if (err)
+               goto out_free;
+       return 0;
+
+out_free:
+       kfree(*nhe);
+       return err;
+}
+
+static void mlx5e_rep_neigh_entry_destroy(struct mlx5e_priv *priv,
+                                         struct mlx5e_neigh_hash_entry *nhe)
+{
+       /* The neigh hash entry must be removed from the hash table regardless
+        * of the reference count value, so it won't be found by the next
+        * neigh notification call. The neigh hash entry reference count is
+        * incremented only during creation and neigh notification calls and
+        * protects from freeing the nhe struct.
+        */
+       mlx5e_rep_neigh_entry_remove(priv, nhe);
+       mlx5e_rep_neigh_entry_release(nhe);
+}
+
+int mlx5e_rep_encap_entry_attach(struct mlx5e_priv *priv,
+                                struct mlx5e_encap_entry *e)
+{
+       struct mlx5e_neigh_hash_entry *nhe;
+       int err;
+
+       nhe = mlx5e_rep_neigh_entry_lookup(priv, &e->m_neigh);
+       if (!nhe) {
+               err = mlx5e_rep_neigh_entry_create(priv, e, &nhe);
+               if (err)
+                       return err;
+       }
+       list_add(&e->encap_list, &nhe->encap_list);
+       return 0;
+}
+
+void mlx5e_rep_encap_entry_detach(struct mlx5e_priv *priv,
+                                 struct mlx5e_encap_entry *e)
+{
+       struct mlx5e_neigh_hash_entry *nhe;
+
+       list_del(&e->encap_list);
+       nhe = mlx5e_rep_neigh_entry_lookup(priv, &e->m_neigh);
+
+       if (list_empty(&nhe->encap_list))
+               mlx5e_rep_neigh_entry_destroy(priv, nhe);
 }
 
 static int mlx5e_rep_open(struct net_device *dev)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
-       struct mlx5_eswitch_rep *rep = priv->ppriv;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        int err;
 
@@ -265,7 +536,8 @@ static int mlx5e_rep_open(struct net_device *dev)
 static int mlx5e_rep_close(struct net_device *dev)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
-       struct mlx5_eswitch_rep *rep = priv->ppriv;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
 
        (void)mlx5_eswitch_set_vport_state(esw, rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN);
@@ -277,7 +549,8 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev,
                                        char *buf, size_t len)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
-       struct mlx5_eswitch_rep *rep = priv->ppriv;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
        int ret;
 
        ret = snprintf(buf, len, "%d", rep->vport - 1);
@@ -320,18 +593,25 @@ static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, u32 handle,
 
 bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv)
 {
-       struct mlx5_eswitch_rep *rep = (struct mlx5_eswitch_rep *)priv->ppriv;
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep;
+
+       if (!MLX5_CAP_GEN(priv->mdev, vport_group_manager))
+               return false;
 
-       if (rep && rep->vport == FDB_UPLINK_VPORT && esw->mode == SRIOV_OFFLOADS)
+       rep = rpriv->rep;
+       if (esw->mode == SRIOV_OFFLOADS &&
+           rep && rep->vport == FDB_UPLINK_VPORT)
                return true;
 
        return false;
 }
 
-bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv)
+static bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv)
 {
-       struct mlx5_eswitch_rep *rep = (struct mlx5_eswitch_rep *)priv->ppriv;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
 
        if (rep && rep->vport != FDB_UPLINK_VPORT)
                return true;
@@ -464,23 +744,20 @@ static void mlx5e_init_rep(struct mlx5_core_dev *mdev,
 static int mlx5e_init_rep_rx(struct mlx5e_priv *priv)
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
-       struct mlx5_eswitch_rep *rep = priv->ppriv;
-       struct mlx5_core_dev *mdev = priv->mdev;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
        struct mlx5_flow_handle *flow_rule;
        int err;
-       int i;
+
+       mlx5e_init_l2_addr(priv);
 
        err = mlx5e_create_direct_rqts(priv);
-       if (err) {
-               mlx5_core_warn(mdev, "create direct rqts failed, %d\n", err);
+       if (err)
                return err;
-       }
 
        err = mlx5e_create_direct_tirs(priv);
-       if (err) {
-               mlx5_core_warn(mdev, "create direct tirs failed, %d\n", err);
+       if (err)
                goto err_destroy_direct_rqts;
-       }
 
        flow_rule = mlx5_eswitch_create_vport_rx_rule(esw,
                                                      rep->vport,
@@ -502,21 +779,19 @@ err_del_flow_rule:
 err_destroy_direct_tirs:
        mlx5e_destroy_direct_tirs(priv);
 err_destroy_direct_rqts:
-       for (i = 0; i < priv->channels.params.num_channels; i++)
-               mlx5e_destroy_rqt(priv, &priv->direct_tir[i].rqt);
+       mlx5e_destroy_direct_rqts(priv);
        return err;
 }
 
 static void mlx5e_cleanup_rep_rx(struct mlx5e_priv *priv)
 {
-       struct mlx5_eswitch_rep *rep = priv->ppriv;
-       int i;
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       struct mlx5_eswitch_rep *rep = rpriv->rep;
 
        mlx5e_tc_cleanup(priv);
        mlx5_del_flow_rules(rep->vport_rx_rule);
        mlx5e_destroy_direct_tirs(priv);
-       for (i = 0; i < priv->channels.params.num_channels; i++)
-               mlx5e_destroy_rqt(priv, &priv->direct_tir[i].rqt);
+       mlx5e_destroy_direct_rqts(priv);
 }
 
 static int mlx5e_init_rep_tx(struct mlx5e_priv *priv)
@@ -545,56 +820,181 @@ static struct mlx5e_profile mlx5e_rep_profile = {
        .cleanup_tx             = mlx5e_cleanup_nic_tx,
        .update_stats           = mlx5e_rep_update_stats,
        .max_nch                = mlx5e_get_rep_max_num_channels,
+       .rx_handlers.handle_rx_cqe       = mlx5e_handle_rx_cqe_rep,
+       .rx_handlers.handle_rx_cqe_mpwqe = NULL /* Not supported */,
        .max_tc                 = 1,
 };
 
-int mlx5e_vport_rep_load(struct mlx5_eswitch *esw,
-                        struct mlx5_eswitch_rep *rep)
+/* e-Switch vport representors */
+
+static int
+mlx5e_nic_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep)
+{
+       struct mlx5e_priv *priv = netdev_priv(rep->netdev);
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+
+       int err;
+
+       if (test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+               err = mlx5e_add_sqs_fwd_rules(priv);
+               if (err)
+                       return err;
+       }
+
+       err = mlx5e_rep_neigh_init(rpriv);
+       if (err)
+               goto err_remove_sqs;
+
+       return 0;
+
+err_remove_sqs:
+       mlx5e_remove_sqs_fwd_rules(priv);
+       return err;
+}
+
+static void
+mlx5e_nic_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep)
+{
+       struct mlx5e_priv *priv = netdev_priv(rep->netdev);
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+
+       if (test_bit(MLX5E_STATE_OPENED, &priv->state))
+               mlx5e_remove_sqs_fwd_rules(priv);
+
+       /* clean (and re-init) existing uplink offloaded TC rules */
+       mlx5e_tc_cleanup(priv);
+       mlx5e_tc_init(priv);
+
+       mlx5e_rep_neigh_cleanup(rpriv);
+}
+
+static int
+mlx5e_vport_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep)
 {
+       struct mlx5e_rep_priv *rpriv;
        struct net_device *netdev;
        int err;
 
-       netdev = mlx5e_create_netdev(esw->dev, &mlx5e_rep_profile, rep);
+       rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL);
+       if (!rpriv)
+               return -ENOMEM;
+
+       netdev = mlx5e_create_netdev(esw->dev, &mlx5e_rep_profile, rpriv);
        if (!netdev) {
                pr_warn("Failed to create representor netdev for vport %d\n",
                        rep->vport);
+               kfree(rpriv);
                return -EINVAL;
        }
 
        rep->netdev = netdev;
+       rpriv->rep = rep;
 
-       err = mlx5e_attach_netdev(esw->dev, netdev);
+       err = mlx5e_attach_netdev(netdev_priv(netdev));
        if (err) {
                pr_warn("Failed to attach representor netdev for vport %d\n",
                        rep->vport);
                goto err_destroy_netdev;
        }
 
+       err = mlx5e_rep_neigh_init(rpriv);
+       if (err) {
+               pr_warn("Failed to initialized neighbours handling for vport %d\n",
+                       rep->vport);
+               goto err_detach_netdev;
+       }
+
        err = register_netdev(netdev);
        if (err) {
                pr_warn("Failed to register representor netdev for vport %d\n",
                        rep->vport);
-               goto err_detach_netdev;
+               goto err_neigh_cleanup;
        }
 
        return 0;
 
+err_neigh_cleanup:
+       mlx5e_rep_neigh_cleanup(rpriv);
+
 err_detach_netdev:
-       mlx5e_detach_netdev(esw->dev, netdev);
+       mlx5e_detach_netdev(netdev_priv(netdev));
 
 err_destroy_netdev:
-       mlx5e_destroy_netdev(esw->dev, netdev_priv(netdev));
-
+       mlx5e_destroy_netdev(netdev_priv(netdev));
+       kfree(rpriv);
        return err;
 
 }
 
-void mlx5e_vport_rep_unload(struct mlx5_eswitch *esw,
-                           struct mlx5_eswitch_rep *rep)
+static void
+mlx5e_vport_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep)
 {
        struct net_device *netdev = rep->netdev;
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       void *ppriv = priv->ppriv;
+
+       unregister_netdev(rep->netdev);
+
+       mlx5e_rep_neigh_cleanup(rpriv);
+       mlx5e_detach_netdev(priv);
+       mlx5e_destroy_netdev(priv);
+       kfree(ppriv); /* mlx5e_rep_priv */
+}
+
+static void mlx5e_rep_register_vf_vports(struct mlx5e_priv *priv)
+{
+       struct mlx5_core_dev *mdev = priv->mdev;
+       struct mlx5_eswitch *esw   = mdev->priv.eswitch;
+       int total_vfs = MLX5_TOTAL_VPORTS(mdev);
+       int vport;
+       u8 mac[ETH_ALEN];
+
+       mlx5_query_nic_vport_mac_address(mdev, 0, mac);
+
+       for (vport = 1; vport < total_vfs; vport++) {
+               struct mlx5_eswitch_rep rep;
+
+               rep.load = mlx5e_vport_rep_load;
+               rep.unload = mlx5e_vport_rep_unload;
+               rep.vport = vport;
+               ether_addr_copy(rep.hw_id, mac);
+               mlx5_eswitch_register_vport_rep(esw, vport, &rep);
+       }
+}
+
+static void mlx5e_rep_unregister_vf_vports(struct mlx5e_priv *priv)
+{
+       struct mlx5_core_dev *mdev = priv->mdev;
+       struct mlx5_eswitch *esw = mdev->priv.eswitch;
+       int total_vfs = MLX5_TOTAL_VPORTS(mdev);
+       int vport;
+
+       for (vport = 1; vport < total_vfs; vport++)
+               mlx5_eswitch_unregister_vport_rep(esw, vport);
+}
+
+void mlx5e_register_vport_reps(struct mlx5e_priv *priv)
+{
+       struct mlx5_core_dev *mdev = priv->mdev;
+       struct mlx5_eswitch *esw   = mdev->priv.eswitch;
+       struct mlx5_eswitch_rep rep;
+
+       mlx5_query_nic_vport_mac_address(mdev, 0, rep.hw_id);
+       rep.load = mlx5e_nic_rep_load;
+       rep.unload = mlx5e_nic_rep_unload;
+       rep.vport = FDB_UPLINK_VPORT;
+       rep.netdev = priv->netdev;
+       mlx5_eswitch_register_vport_rep(esw, 0, &rep); /* UPLINK PF vport*/
+
+       mlx5e_rep_register_vf_vports(priv); /* VFs vports */
+}
+
+void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv)
+{
+       struct mlx5_core_dev *mdev = priv->mdev;
+       struct mlx5_eswitch *esw   = mdev->priv.eswitch;
 
-       unregister_netdev(netdev);
-       mlx5e_detach_netdev(esw->dev, netdev);
-       mlx5e_destroy_netdev(esw->dev, netdev_priv(netdev));
+       mlx5e_rep_unregister_vf_vports(priv); /* VFs vports */
+       mlx5_eswitch_unregister_vport_rep(esw, 0); /* UPLINK PF*/
 }