From: Valentine Fatiev <valentinef@nvidia.com>
Subject: [PATCH] BACKPORT: drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c

Change-Id: I18a276c7c5a260337c6236a192e826a45cd4f0fb
---
 .../ethernet/mellanox/mlx5/core/en/rep/tc.c   | 310 +++++++++++++++++-
 1 file changed, 301 insertions(+), 9 deletions(-)

--- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c
@@ -25,8 +25,9 @@
 struct mlx5e_rep_indr_block_priv {
 	struct net_device *netdev;
 	struct mlx5e_rep_priv *rpriv;
+#if defined( HAVE_TC_BLOCK_OFFLOAD) || defined(HAVE_FLOW_BLOCK_OFFLOAD)
 	enum flow_block_binder_type binder_type;
-
+#endif
 	struct list_head list;
 };
 
@@ -128,8 +129,13 @@ unlock:
 	mlx5e_put_flow_list(priv, &flow_list);
 }
 
+#if defined(HAVE_TC_FLOWER_OFFLOAD) || defined(HAVE_FLOW_CLS_OFFLOAD)
 static int
+#if defined( HAVE_TC_BLOCK_OFFLOAD) || defined(HAVE_FLOW_BLOCK_OFFLOAD)
 mlx5e_rep_setup_tc_cls_flower(struct mlx5e_priv *priv,
+#else
+mlx5e_rep_setup_tc_cls_flower(struct net_device *dev,
+#endif
 			      struct flow_cls_offload *cls_flower, int flags)
 {
 	switch (cls_flower->command) {
@@ -139,14 +145,18 @@ mlx5e_rep_setup_tc_cls_flower(struct mlx
 	case FLOW_CLS_DESTROY:
 		return mlx5e_delete_flower(priv->netdev, priv, cls_flower,
 					   flags);
+#ifdef HAVE_TC_CLSFLOWER_STATS
 	case FLOW_CLS_STATS:
 		return mlx5e_stats_flower(priv->netdev, priv, cls_flower,
 					  flags);
+#endif
 	default:
 		return -EOPNOTSUPP;
 	}
 }
+#endif /* defined(HAVE_TC_FLOWER_OFFLOAD) */
 
+#ifdef HAVE_TC_CLSMATCHALL_STATS
 static void mlx5e_tc_stats_matchall(struct mlx5e_priv *priv,
 				    struct tc_cls_matchall_offload *ma)
 {
@@ -157,8 +167,17 @@ static void mlx5e_tc_stats_matchall(stru
 	dpkts = priv->stats.rep_stats.vport_rx_packets - rpriv->prev_vf_vport_stats.rx_packets;
 	dbytes = priv->stats.rep_stats.vport_rx_bytes - rpriv->prev_vf_vport_stats.rx_bytes;
 	mlx5e_stats_copy_rep_stats(&rpriv->prev_vf_vport_stats, &priv->stats.rep_stats);
+#ifdef HAVE_FLOW_STATS_UPDATE_6_PARAMS
 	flow_stats_update(&ma->stats, dbytes, dpkts, 0, jiffies,
 			  FLOW_ACTION_HW_STATS_DELAYED);
+#elif defined(HAVE_FLOW_STATS_UPDATE_5_PARAMS)
+	flow_stats_update(&ma->stats, dbytes, dpkts, jiffies,
+			FLOW_ACTION_HW_STATS_DELAYED);
+#elif defined(HAVE_TC_SETUP_FLOW_ACTION)
+	flow_stats_update(&ma->stats, dbytes, dpkts, jiffies);
+#else
+	tcf_exts_stats_update(ma->exts, dbytes, dpkts, jiffies);
+#endif
 }
 
 static
@@ -177,6 +196,9 @@ int mlx5e_rep_setup_tc_cls_matchall(stru
 		return -EOPNOTSUPP;
 	}
 }
+#endif /* HAVE_TC_CLSMATCHALL_STATS */
+
+#if defined(HAVE_TC_BLOCK_OFFLOAD) || defined(HAVE_FLOW_CLS_OFFLOAD)
 
 static int mlx5e_rep_setup_tc_cb(enum tc_setup_type type, void *type_data,
 				 void *cb_priv)
@@ -190,13 +212,76 @@ static int mlx5e_rep_setup_tc_cb(enum tc
 	switch (type) {
 	case TC_SETUP_CLSFLOWER:
 		return mlx5e_rep_setup_tc_cls_flower(priv, type_data, flags);
+#ifdef HAVE_TC_CLSMATCHALL_STATS
 	case TC_SETUP_CLSMATCHALL:
 		return mlx5e_rep_setup_tc_cls_matchall(priv, type_data);
+#endif
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+#ifdef HAVE_FLOW_CLS_OFFLOAD
+static LIST_HEAD(mlx5e_rep_block_cb_list);
+#endif
+
+#ifndef HAVE_FLOW_BLOCK_CB_SETUP_SIMPLE
+static int mlx5e_rep_setup_tc_block(struct net_device *dev,
+				    struct tc_block_offload *f)
+{
+	struct mlx5e_priv *priv = netdev_priv(dev);
+#ifdef HAVE_FLOW_CLS_OFFLOAD
+	struct flow_block_cb *block_cb;
+#endif
+
+	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+		return -EOPNOTSUPP;
+
+#ifdef HAVE_FLOW_CLS_OFFLOAD
+	f->driver_block_list = &mlx5e_rep_block_cb_list;
+#endif
+
+	switch (f->command) {
+	case TC_BLOCK_BIND:
+#ifdef HAVE_FLOW_CLS_OFFLOAD
+		block_cb = flow_block_cb_alloc(mlx5e_rep_setup_tc_cb, priv, priv, NULL);
+#else
+		return tcf_block_cb_register(f->block, mlx5e_rep_setup_tc_cb,
+#ifdef HAVE_TC_BLOCK_OFFLOAD_EXTACK
+					     priv, priv, f->extack);
+#else
+
+					     priv, priv);
+#endif
+#endif /* HAVE_FLOW_CLS_OFFLOAD */
+#ifdef HAVE_FLOW_CLS_OFFLOAD
+                if (IS_ERR(block_cb)) {
+                        return -ENOENT;
+                }
+                flow_block_cb_add(block_cb, f);
+                list_add_tail(&block_cb->driver_list, f->driver_block_list);
+                return 0;
+#endif
+	case TC_BLOCK_UNBIND:
+#ifndef HAVE_FLOW_CLS_OFFLOAD
+		tcf_block_cb_unregister(f->block, mlx5e_rep_setup_tc_cb, priv);
+#else
+		block_cb = flow_block_cb_lookup(f->block, mlx5e_rep_setup_tc_cb, priv);
+		if (!block_cb)
+			return -ENOENT;
+
+		flow_block_cb_remove(block_cb, f);
+		list_del(&block_cb->driver_list);
+#endif
+		return 0;
 	default:
 		return -EOPNOTSUPP;
 	}
 }
+#endif /* HAVE_FLOW_BLOCK_CB_SETUP_SIMPLE */
+#endif /* HAVE_TC_BLOCK_OFFLOAD */
 
+#ifdef HAVE_TC_SETUP_FT
 static int mlx5e_rep_setup_ft_cb(enum tc_setup_type type, void *type_data,
 				 void *cb_priv)
 {
@@ -241,32 +326,86 @@ static int mlx5e_rep_setup_ft_cb(enum tc
 		return -EOPNOTSUPP;
 	}
 }
+#endif
 
+#if defined(HAVE_TC_FLOWER_OFFLOAD) || defined(HAVE_FLOW_CLS_OFFLOAD)
 static LIST_HEAD(mlx5e_rep_block_tc_cb_list);
 static LIST_HEAD(mlx5e_rep_block_ft_cb_list);
 int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type,
 		       void *type_data)
 {
+#ifdef HAVE_FLOW_BLOCK_CB_SETUP_SIMPLE
 	struct mlx5e_priv *priv = netdev_priv(dev);
-	struct flow_block_offload *f = type_data;
+#endif
 
+#if !defined(HAVE_TC_BLOCK_OFFLOAD) && ! defined(HAVE_FLOW_BLOCK_OFFLOAD)
+	unsigned long flags = MLX5_TC_FLAG(INGRESS) | MLX5_TC_FLAG(ESW_OFFLOAD);
+#endif
+#ifdef HAVE_UNLOCKED_DRIVER_CB
+	struct flow_block_offload *f = type_data;
 	f->unlocked_driver_cb = true;
+#endif
 
 	switch (type) {
+#if defined(HAVE_TC_BLOCK_OFFLOAD) || defined(HAVE_FLOW_BLOCK_OFFLOAD)
 	case TC_SETUP_BLOCK:
+#ifdef HAVE_FLOW_BLOCK_CB_SETUP_SIMPLE
 		return flow_block_cb_setup_simple(type_data,
 						  &mlx5e_rep_block_tc_cb_list,
 						  mlx5e_rep_setup_tc_cb,
 						  priv, priv, true);
+#else
+		return mlx5e_rep_setup_tc_block(dev, type_data);
+#endif /* HAVE_FLOW_BLOCK_CB_SETUP_SIMPLE */
+#else /* HAVE_TC_BLOCK_OFFLOAD || HAVE_FLOW_BLOCK_OFFLOAD */
+	case TC_SETUP_CLSFLOWER:
+		return mlx5e_rep_setup_tc_cls_flower(dev, type_data, flags);
+#endif /* HAVE_TC_BLOCK_OFFLOAD || HAVE_FLOW_BLOCK_OFFLOAD */
+#ifdef HAVE_TC_SETUP_FT
 	case TC_SETUP_FT:
 		return flow_block_cb_setup_simple(type_data,
 						  &mlx5e_rep_block_ft_cb_list,
 						  mlx5e_rep_setup_ft_cb,
 						  priv, priv, true);
+#endif /* HAVE_TC_SETUP_FT */
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+#endif
+
+#ifdef HAVE_TC_SETUP_CB_EGDEV_REGISTER
+#ifdef HAVE_TC_BLOCK_OFFLOAD
+int mlx5e_rep_setup_tc_cb_egdev(enum tc_setup_type type, void *type_data,
+				void *cb_priv)
+{
+	unsigned long flags = MLX5_TC_FLAG(EGRESS) | MLX5_TC_FLAG(ESW_OFFLOAD);
+	struct mlx5e_priv *priv = cb_priv;
+
+#ifdef HAVE_TC_INDR_API
+	/* some rhel kernels have indirect offload and egdev,
+	 * so dont use egdev. e.g. rhel8.0
+	 */
+	return -EOPNOTSUPP;
+#endif
+
+	switch (type) {
+	case TC_SETUP_CLSFLOWER:
+		return mlx5e_rep_setup_tc_cls_flower(priv, type_data, flags);
 	default:
 		return -EOPNOTSUPP;
 	}
 }
+#else
+int mlx5e_rep_setup_tc_cb(enum tc_setup_type type, void *type_data,
+			  void *cb_priv)
+{
+	struct net_device *dev = cb_priv;
+
+	return mlx5e_setup_tc(dev, type, type_data);
+}
+#endif
+#endif
 
 int mlx5e_rep_tc_init(struct mlx5e_rep_priv *rpriv)
 {
@@ -312,6 +451,7 @@ int mlx5e_rep_tc_event_port_affinity(str
 	return NOTIFY_OK;
 }
 
+#if defined( HAVE_TC_BLOCK_OFFLOAD) || defined(HAVE_FLOW_BLOCK_OFFLOAD)
 static struct mlx5e_rep_indr_block_priv *
 mlx5e_rep_indr_block_priv_lookup(struct mlx5e_rep_priv *rpriv,
 				 struct net_device *netdev,
@@ -377,6 +517,7 @@ static int mlx5e_rep_indr_setup_tc_cb(en
 	}
 }
 
+#ifdef HAVE_TC_SETUP_FT
 static int mlx5e_rep_indr_setup_ft_cb(enum tc_setup_type type,
 				      void *type_data, void *indr_priv)
 {
@@ -422,7 +563,9 @@ static int mlx5e_rep_indr_setup_ft_cb(en
 		return -EOPNOTSUPP;
 	}
 }
+#endif
 
+#ifdef HAVE_FLOW_BLOCK_CB_ALLOC
 static void mlx5e_rep_indr_block_unbind(void *cb_priv)
 {
 	struct mlx5e_rep_indr_block_priv *indr_priv = cb_priv;
@@ -430,6 +573,7 @@ static void mlx5e_rep_indr_block_unbind(
 	list_del(&indr_priv->list);
 	kfree(indr_priv);
 }
+#endif
 
 static LIST_HEAD(mlx5e_block_cb_list);
 
@@ -488,14 +632,22 @@ mlx5e_rep_indr_setup_block(struct net_de
 			   void *data,
 			   void (*cleanup)(struct flow_block_cb *block_cb))
 {
-	struct mlx5e_rep_indr_block_priv *indr_priv;
+	struct mlx5e_rep_indr_block_priv *indr_priv = NULL;
+#ifdef HAVE_FLOW_BLOCK_CB_ALLOC
 	struct flow_block_cb *block_cb;
+#else
+	int err = 0;
+#endif
 
 	if (!mlx5e_rep_check_indr_block_supported(rpriv, netdev, f))
 		return -EOPNOTSUPP;
 
+#ifdef HAVE_UNLOCKED_DRIVER_CB
 	f->unlocked_driver_cb = true;
+#endif
+#ifdef HAVE_FLOW_BLOCK_OFFLOAD
 	f->driver_block_list = &mlx5e_block_cb_list;
+#endif
 
 	switch (f->command) {
 	case FLOW_BLOCK_BIND:
@@ -513,10 +665,16 @@ mlx5e_rep_indr_setup_block(struct net_de
 		list_add(&indr_priv->list,
 			 &rpriv->uplink_priv.tc_indr_block_priv_list);
 
+#ifdef HAVE_FLOW_BLOCK_CB_ALLOC
+#ifdef HAVE_FLOW_INDR_BLOCK_CB_ALLOC
 		block_cb = flow_indr_block_cb_alloc(setup_cb, indr_priv, indr_priv,
 						    mlx5e_rep_indr_block_unbind,
 						    f, netdev, sch, data, rpriv,
 						    cleanup);
+#else
+		block_cb = flow_block_cb_alloc(setup_cb, indr_priv, indr_priv,
+					       mlx5e_rep_indr_block_unbind);
+#endif
 		if (IS_ERR(block_cb)) {
 			list_del(&indr_priv->list);
 			kfree(indr_priv);
@@ -526,17 +684,42 @@ mlx5e_rep_indr_setup_block(struct net_de
 		list_add_tail(&block_cb->driver_list, &mlx5e_block_cb_list);
 
 		return 0;
+#else
+		err = tcf_block_cb_register(f->block,
+					    mlx5e_rep_indr_setup_tc_cb,
+					    indr_priv, indr_priv
+#ifdef HAVE_TC_BLOCK_OFFLOAD_EXTACK
+					    , f->extack
+#endif
+					   );
+		if (err) {
+			list_del(&indr_priv->list);
+			kfree(indr_priv);
+		}
+
+		return err;
+#endif
+
 	case FLOW_BLOCK_UNBIND:
 		indr_priv = mlx5e_rep_indr_block_priv_lookup(rpriv, netdev, f->binder_type);
 		if (!indr_priv)
 			return -ENOENT;
 
+#ifdef HAVE_FLOW_BLOCK_CB_ALLOC
 		block_cb = flow_block_cb_lookup(f->block, setup_cb, indr_priv);
 		if (!block_cb)
 			return -ENOENT;
 
 		flow_indr_block_cb_remove(block_cb, f);
 		list_del(&block_cb->driver_list);
+#else
+		tcf_block_cb_unregister(f->block,
+					mlx5e_rep_indr_setup_tc_cb,
+					indr_priv);
+		list_del(&indr_priv->list);
+		kfree(indr_priv);
+#endif
+
 		return 0;
 	default:
 		return -EOPNOTSUPP;
@@ -544,6 +727,7 @@ mlx5e_rep_indr_setup_block(struct net_de
 	return 0;
 }
 
+#ifdef HAVE_FLOW_OFFLOAD_ACTION
 static int
 mlx5e_rep_indr_replace_act(struct mlx5e_rep_priv *rpriv,
 			   struct flow_offload_action *fl_act)
@@ -641,6 +825,7 @@ mlx5e_rep_indr_setup_act(struct mlx5e_re
 		return -EOPNOTSUPP;
 	}
 }
+#endif /* HAVE_FLOW_OFFLOAD_ACTION */
 
 static int
 mlx5e_rep_indr_no_dev_setup(struct mlx5e_rep_priv *rpriv,
@@ -651,19 +836,36 @@ mlx5e_rep_indr_no_dev_setup(struct mlx5e
 		return -EOPNOTSUPP;
 
 	switch (type) {
+#ifdef HAVE_FLOW_OFFLOAD_ACTION
 	case TC_SETUP_ACT:
 		return mlx5e_rep_indr_setup_act(rpriv, data);
+#endif
 	default:
 		return -EOPNOTSUPP;
 	}
 }
 
 static
+#ifdef HAVE_FLOW_INDR_BLOCK_BIND_CB_T_7_PARAMS
 int mlx5e_rep_indr_setup_cb(struct net_device *netdev, struct Qdisc *sch, void *cb_priv,
-			    enum tc_setup_type type, void *type_data,
-			    void *data,
-			    void (*cleanup)(struct flow_block_cb *block_cb))
+#else
+int mlx5e_rep_indr_setup_cb(struct net_device *netdev, void *cb_priv,
+#endif
+       		    	    enum tc_setup_type type, void *type_data
+#if !defined(HAVE_FLOW_INDR_BLOCK_BIND_CB_T_4_PARAMS) && defined(HAVE_FLOW_INDR_DEV_REGISTER)
+			    , void *data,
+			    void (*cleanup)(struct flow_block_cb *block_cb)
+#endif
+			    )
 {
+#ifndef HAVE_FLOW_INDR_BLOCK_BIND_CB_T_7_PARAMS
+	struct Qdisc *sch = NULL;
+#endif
+#if defined(HAVE_FLOW_INDR_BLOCK_BIND_CB_T_4_PARAMS) || !defined(HAVE_FLOW_INDR_DEV_REGISTER)
+	void *data = NULL;
+	void *cleanup = NULL;
+#endif
+
 	if (!netdev)
 		return mlx5e_rep_indr_no_dev_setup(cb_priv, type, data);
 
@@ -672,15 +874,78 @@ int mlx5e_rep_indr_setup_cb(struct net_d
 		return mlx5e_rep_indr_setup_block(netdev, sch, cb_priv, type_data,
 						  mlx5e_rep_indr_setup_tc_cb,
 						  data, cleanup);
+#ifdef HAVE_TC_SETUP_FT
 	case TC_SETUP_FT:
 		return mlx5e_rep_indr_setup_block(netdev, sch, cb_priv, type_data,
 						  mlx5e_rep_indr_setup_ft_cb,
 						  data, cleanup);
+#endif
 	default:
 		return -EOPNOTSUPP;
 	}
 }
 
+#ifndef HAVE_FLOW_INDR_DEV_REGISTER
+static int mlx5e_rep_indr_register_block(struct mlx5e_rep_priv *rpriv,
+					 struct net_device *netdev)
+{
+	int err;
+
+	err = __flow_indr_block_cb_register(netdev, rpriv,
+					    mlx5e_rep_indr_setup_cb,
+					    rpriv);
+	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 mlx5e_rep_priv *rpriv,
+					    struct net_device *netdev)
+{
+	__flow_indr_block_cb_unregister(netdev, mlx5e_rep_indr_setup_cb,
+					rpriv);
+}
+
+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(rpriv, cb_priv->netdev);
+		kfree(cb_priv);
+	}
+}
+
+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) &&
+	    !(is_vlan_dev(netdev) && vlan_dev_real_dev(netdev) == rpriv->netdev) &&
+	    !netif_is_ovs_master(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(rpriv, netdev);
+		break;
+	}
+	return NOTIFY_OK;
+}
+#endif /* HAVE_FLOW_INDR_DEV_REGISTER */
+
 int mlx5e_rep_tc_netdevice_event_register(struct mlx5e_rep_priv *rpriv)
 {
 	struct mlx5_rep_uplink_priv *uplink_priv = &rpriv->uplink_priv;
@@ -688,14 +953,35 @@ int mlx5e_rep_tc_netdevice_event_registe
 	/* init indirect block notifications */
 	INIT_LIST_HEAD(&uplink_priv->tc_indr_block_priv_list);
 
+#ifdef HAVE_FLOW_INDR_DEV_REGISTER
 	return flow_indr_dev_register(mlx5e_rep_indr_setup_cb, rpriv);
+#else
+	uplink_priv->netdevice_nb.notifier_call = mlx5e_nic_rep_netdevice_event;
+	return register_netdevice_notifier_dev_net(rpriv->netdev,
+						   &uplink_priv->netdevice_nb,
+						   &uplink_priv->netdevice_nn);
+#endif
 }
 
 void mlx5e_rep_tc_netdevice_event_unregister(struct mlx5e_rep_priv *rpriv)
 {
+#ifndef HAVE_FLOW_INDR_DEV_REGISTER
+	struct mlx5_rep_uplink_priv *uplink_priv = &rpriv->uplink_priv;
+
+	/* clean indirect TC block notifications */
+	unregister_netdevice_notifier_dev_net(rpriv->netdev,
+					      &uplink_priv->netdevice_nb,
+					      &uplink_priv->netdevice_nn);
+#else
 	flow_indr_dev_unregister(mlx5e_rep_indr_setup_cb, rpriv,
+#ifdef HAVE_FLOW_INDR_DEV_UNREGISTER_FLOW_SETUP_CB_T
+				 mlx5e_rep_indr_setup_tc_cb);
+#else
 				 mlx5e_rep_indr_block_unbind);
-}
+#endif
+#endif
+ }
+#endif /* HAVE_TC_BLOCK_OFFLOAD || HAVE_FLOW_BLOCK_OFFLOAD */
 
 void mlx5e_rep_tc_receive(struct mlx5_cqe64 *cqe, struct mlx5e_rq *rq,
 			  struct sk_buff *skb)
@@ -754,11 +1040,17 @@ forward:
 	else
 		napi_gro_receive(rq->cq.napi, skb);
 
-	dev_put(tc_priv.fwd_dev);
+#ifndef HAVE_NETDEV_PUT
+	if (tc_priv.fwd_dev)
+#endif
+		dev_put(tc_priv.fwd_dev);
 
 	return;
 
 free_skb:
-	dev_put(tc_priv.fwd_dev);
+#ifndef HAVE_NETDEV_PUT
+	if (tc_priv.fwd_dev)
+#endif
+		dev_put(tc_priv.fwd_dev);
 	dev_kfree_skb_any(skb);
 }
