static const char mlx5e_rep_driver_name[] = "mlx5e_rep";
 
+struct mlx5e_rep_indr_block_priv {
+       struct net_device *netdev;
+       struct mlx5e_rep_priv *rpriv;
+
+       struct list_head list;
+};
+
+static void mlx5e_rep_indr_unregister_block(struct net_device *netdev);
+
 static void mlx5e_rep_get_drvinfo(struct net_device *dev,
                                  struct ethtool_drvinfo *drvinfo)
 {
        neigh_release(n);
 }
 
+static struct mlx5e_rep_indr_block_priv *
+mlx5e_rep_indr_block_priv_lookup(struct mlx5e_rep_priv *rpriv,
+                                struct net_device *netdev)
+{
+       struct mlx5e_rep_indr_block_priv *cb_priv;
+
+       /* All callback list access should be protected by RTNL. */
+       ASSERT_RTNL();
+
+       list_for_each_entry(cb_priv,
+                           &rpriv->uplink_priv.tc_indr_block_priv_list,
+                           list)
+               if (cb_priv->netdev == netdev)
+                       return cb_priv;
+
+       return NULL;
+}
+
+static void mlx5e_rep_indr_clean_block_privs(struct mlx5e_rep_priv *rpriv)
+{
+       struct mlx5e_rep_indr_block_priv *cb_priv, *temp;
+       struct list_head *head = &rpriv->uplink_priv.tc_indr_block_priv_list;
+
+       list_for_each_entry_safe(cb_priv, temp, head, list) {
+               mlx5e_rep_indr_unregister_block(cb_priv->netdev);
+               kfree(cb_priv);
+       }
+}
+
+static int
+mlx5e_rep_indr_offload(struct net_device *netdev,
+                      struct tc_cls_flower_offload *flower,
+                      struct mlx5e_rep_indr_block_priv *indr_priv)
+{
+       return -EOPNOTSUPP;
+}
+
+static int mlx5e_rep_indr_setup_block_cb(enum tc_setup_type type,
+                                        void *type_data, void *indr_priv)
+{
+       struct mlx5e_rep_indr_block_priv *priv = indr_priv;
+
+       switch (type) {
+       case TC_SETUP_CLSFLOWER:
+               return mlx5e_rep_indr_offload(priv->netdev, type_data, priv);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int
+mlx5e_rep_indr_setup_tc_block(struct net_device *netdev,
+                             struct mlx5e_rep_priv *rpriv,
+                             struct tc_block_offload *f)
+{
+       struct mlx5e_rep_indr_block_priv *indr_priv;
+       int err = 0;
+
+       if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+               return -EOPNOTSUPP;
+
+       switch (f->command) {
+       case TC_BLOCK_BIND:
+               indr_priv = mlx5e_rep_indr_block_priv_lookup(rpriv, netdev);
+               if (indr_priv)
+                       return -EEXIST;
+
+               indr_priv = kmalloc(sizeof(*indr_priv), GFP_KERNEL);
+               if (!indr_priv)
+                       return -ENOMEM;
+
+               indr_priv->netdev = netdev;
+               indr_priv->rpriv = rpriv;
+               list_add(&indr_priv->list,
+                        &rpriv->uplink_priv.tc_indr_block_priv_list);
+
+               err = tcf_block_cb_register(f->block,
+                                           mlx5e_rep_indr_setup_block_cb,
+                                           netdev, indr_priv, f->extack);
+               if (err) {
+                       list_del(&indr_priv->list);
+                       kfree(indr_priv);
+               }
+
+               return err;
+       case TC_BLOCK_UNBIND:
+               tcf_block_cb_unregister(f->block,
+                                       mlx5e_rep_indr_setup_block_cb,
+                                       netdev);
+               indr_priv = mlx5e_rep_indr_block_priv_lookup(rpriv, netdev);
+               if (indr_priv) {
+                       list_del(&indr_priv->list);
+                       kfree(indr_priv);
+               }
+
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static
+int mlx5e_rep_indr_setup_tc_cb(struct net_device *netdev, void *cb_priv,
+                              enum tc_setup_type type, void *type_data)
+{
+       switch (type) {
+       case TC_SETUP_BLOCK:
+               return mlx5e_rep_indr_setup_tc_block(netdev, cb_priv,
+                                                     type_data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int mlx5e_rep_indr_register_block(struct mlx5e_rep_priv *rpriv,
+                                        struct net_device *netdev)
+{
+       int err;
+
+       err = __tc_indr_block_cb_register(netdev, rpriv,
+                                         mlx5e_rep_indr_setup_tc_cb,
+                                         netdev);
+       if (err) {
+               struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
+
+               mlx5_core_err(priv->mdev, "Failed to register remote block notifier for %s err=%d\n",
+                             netdev_name(netdev), err);
+       }
+       return err;
+}
+
+static void mlx5e_rep_indr_unregister_block(struct net_device *netdev)
+{
+       __tc_indr_block_cb_unregister(netdev, mlx5e_rep_indr_setup_tc_cb,
+                                     netdev);
+}
+
+static int mlx5e_nic_rep_netdevice_event(struct notifier_block *nb,
+                                        unsigned long event, void *ptr)
+{
+       struct mlx5e_rep_priv *rpriv = container_of(nb, struct mlx5e_rep_priv,
+                                                    uplink_priv.netdevice_nb);
+       struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
+       struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+
+       if (!mlx5e_tc_tun_device_to_offload(priv, netdev))
+               return NOTIFY_OK;
+
+       switch (event) {
+       case NETDEV_REGISTER:
+               mlx5e_rep_indr_register_block(rpriv, netdev);
+               break;
+       case NETDEV_UNREGISTER:
+               mlx5e_rep_indr_unregister_block(netdev);
+               break;
+       }
+       return NOTIFY_OK;
+}
+
 static struct mlx5e_neigh_hash_entry *
 mlx5e_rep_neigh_entry_lookup(struct mlx5e_priv *priv,
                             struct mlx5e_neigh *m_neigh);
        if (err)
                goto  err_neigh_cleanup;
 
+       /* init indirect block notifications */
+       INIT_LIST_HEAD(&uplink_priv->tc_indr_block_priv_list);
+       uplink_priv->netdevice_nb.notifier_call = mlx5e_nic_rep_netdevice_event;
+       err = register_netdevice_notifier(&uplink_priv->netdevice_nb);
+       if (err) {
+               mlx5_core_err(priv->mdev, "Failed to register netdev notifier\n");
+               goto err_indirect_block_cleanup;
+       }
+
        return 0;
 
+err_indirect_block_cleanup:
+       mlx5e_tc_esw_cleanup(&uplink_priv->tc_ht);
 err_neigh_cleanup:
        mlx5e_rep_neigh_cleanup(rpriv);
 err_remove_sqs:
        if (test_bit(MLX5E_STATE_OPENED, &priv->state))
                mlx5e_remove_sqs_fwd_rules(priv);
 
+       /* clean indirect TC block notifications */
+       unregister_netdevice_notifier(&rpriv->uplink_priv.netdevice_nb);
+       mlx5e_rep_indr_clean_block_privs(rpriv);
+
        /* clean uplink offloaded TC rules, delete shared tc flow table */
        mlx5e_tc_esw_cleanup(&rpriv->uplink_priv.tc_ht);