[][openwrt][mt7988][tops][tnl-offload: support with crypto-eip]

[Description]
Add tunnel offload with crypto-eip support.

Tunnel parameter now records CDRT index to provide help for tunnel
decapsulation and encapsulation offload setup flow.

For decapsulation, If skb already carried with CDRT information, it means
that packet is already decrypted by HW offload engine crypto-eip and there
is already a CLS rule setup for that packet. Therefore, we should not
modify the exist CLS rule. Instead, we just need to update the TPORT to
that CLS entry.

For encapsulation, there is nothing more need to do.

[Release-log]
N/A

Change-Id: Ib4ff408836fa0ed16895fc486c5ebdee90cfa0b6
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7923905
diff --git a/package-21.02/kernel/tops/src/inc/tunnel.h b/package-21.02/kernel/tops/src/inc/tunnel.h
index 387d923..d00eebe 100644
--- a/package-21.02/kernel/tops/src/inc/tunnel.h
+++ b/package-21.02/kernel/tops/src/inc/tunnel.h
@@ -127,6 +127,7 @@
 	u16 protocol;
 	u8 tops_entry_proto;
 	u8 cls_entry;
+	u8 cdrt;
 	u8 flag; /* bit: enum tops_tnl_params_flag */
 	union {
 		struct l2tp_param l2tp; /* 4B */
@@ -147,6 +148,13 @@
 	u32 flag; /* bit: enum tops_tnl_info_flag */
 } __aligned(16);
 
+/*
+ * tnl_l2_param_update:
+ *	update tunnel l2 info only
+ *	return 1 on l2 params have difference
+ *	return 0 on l2 params are the same
+ *	return negative value on error
+ */
 struct tops_tnl_type {
 	const char *type_name;
 	enum tops_entry_type tops_entry;
@@ -162,6 +170,8 @@
 				     struct tops_tnl_params *tnl_params);
 	int (*tnl_debug_param_setup)(const char *buf, int *ofs,
 				     struct tops_tnl_params *tnl_params);
+	int (*tnl_l2_param_update)(struct sk_buff *skb,
+				   struct tops_tnl_params *tnl_params);
 	int (*tnl_dump_param)(char *buf, struct tops_tnl_params *tnl_params);
 	bool (*tnl_info_match)(struct tops_tnl_params *params1,
 			       struct tops_tnl_params *params2);
diff --git a/package-21.02/kernel/tops/src/tnl_offload.c b/package-21.02/kernel/tops/src/tnl_offload.c
index c3b2cdf..bd37faa 100644
--- a/package-21.02/kernel/tops/src/tnl_offload.c
+++ b/package-21.02/kernel/tops/src/tnl_offload.c
@@ -22,6 +22,8 @@
 #include <mtk_hnat/hnat.h>
 #include <mtk_hnat/nf_hnat_mtk.h>
 
+#include <pce/cdrt.h>
+#include <pce/cls.h>
 #include <pce/dipfilter.h>
 #include <pce/pce.h>
 
@@ -123,7 +125,7 @@
 
 static inline bool skb_tops_valid(struct sk_buff *skb)
 {
-	return (skb && skb_hnat_tops(skb) <= __TOPS_ENTRY_MAX);
+	return (skb && skb_hnat_tops(skb) < __TOPS_ENTRY_MAX);
 }
 
 static inline struct tops_tnl_type *skb_to_tnl_type(struct sk_buff *skb)
@@ -139,6 +141,19 @@
 	return tnl_type ? tnl_type : ERR_PTR(-ENODEV);
 }
 
+static inline struct tops_tnl_info *skb_to_tnl_info(struct sk_buff *skb)
+{
+	u32 tnl_idx = skb_hnat_tops(skb) - __TOPS_ENTRY_MAX;
+
+	if (tnl_idx >= CONFIG_TOPS_TNL_NUM)
+		return ERR_PTR(-EINVAL);
+
+	if (!test_bit(tnl_idx, tops_tnl.tnl_used))
+		return ERR_PTR(-EACCES);
+
+	return &tops_tnl.tnl_infos[tnl_idx];
+}
+
 static inline void skb_mark_unbind(struct sk_buff *skb)
 {
 	skb_hnat_tops(skb) = 0;
@@ -331,25 +346,30 @@
 	unsigned long flag;
 
 	tnl_info->tnl_params.cls_entry = tnl_info->tcls->cls->idx;
-	TOPS_NOTICE("cls entry: %u\n", tnl_info->tcls->cls->idx);
 
 	spin_lock_irqsave(&tnl_info->lock, flag);
 	tnl_info->cache.cls_entry = tnl_info->tcls->cls->idx;
 	spin_unlock_irqrestore(&tnl_info->lock, flag);
 }
 
-static void mtk_tops_tnl_info_cls_entry_unprepare(struct tops_tnl_info *tnl_info)
+static void mtk_tops_tnl_info_cls_entry_unprepare(struct tops_tnl_info *tnl_info,
+						  struct tops_tnl_params *tnl_params)
 {
 	struct tops_cls_entry *tcls = tnl_info->tcls;
 
-	pr_notice("cls entry unprepare\n");
 	tnl_info->tcls = NULL;
 
 	if (refcount_dec_and_test(&tcls->refcnt)) {
-		pr_notice("cls entry delete\n");
 		list_del(&tcls->node);
 
-		memset(&tcls->cls->cdesc, 0, sizeof(tcls->cls->cdesc));
+		if (!tnl_params->cdrt)
+			memset(&tcls->cls->cdesc, 0, sizeof(tcls->cls->cdesc));
+		else
+			/*
+			 * recover tport_ix to let match packets to
+			 * go through EIP197 only
+			 */
+			CLS_DESC_DATA(&tcls->cls->cdesc, tport_idx, 2);
 
 		mtk_pce_cls_entry_write(tcls->cls);
 
@@ -360,7 +380,8 @@
 }
 
 static struct tops_cls_entry *
-mtk_tops_tnl_info_cls_entry_prepare(struct tops_tnl_info *tnl_info)
+mtk_tops_tnl_info_cls_entry_prepare(struct tops_tnl_info *tnl_info,
+				    struct tops_tnl_params *tnl_params)
 {
 	struct tops_cls_entry *tcls;
 	int ret;
@@ -369,10 +390,21 @@
 	if (!tcls)
 		return ERR_PTR(-ENOMEM);
 
+	if (!tnl_params->cdrt) {
+		tcls->cls = mtk_pce_cls_entry_alloc();
+		if (IS_ERR(tcls->cls)) {
+			ret = PTR_ERR(tcls->cls);
+			goto free_tcls;
+		}
+	} else {
+		struct cdrt_entry *cdrt = mtk_pce_cdrt_entry_find(tnl_params->cdrt);
+
-	tcls->cls = mtk_pce_cls_entry_alloc();
-	if (IS_ERR(tcls->cls)) {
-		ret = PTR_ERR(tcls->cls);
-		goto free_tcls;
+		if (IS_ERR(cdrt)) {
+			ret = PTR_ERR(cdrt);
+			goto free_tcls;
+		}
+
+		tcls->cls = cdrt->cls;
 	}
 
 	INIT_LIST_HEAD(&tcls->node);
@@ -397,10 +429,8 @@
 		return -EINVAL;
 
 	ret = mtk_pce_cls_entry_write(tnl_info->tcls->cls);
-	if (ret) {
-		mtk_tops_tnl_info_cls_entry_unprepare(tnl_info);
+	if (ret)
 		return ret;
-	}
 
 	tnl_info->tcls->updated = true;
 
@@ -409,9 +439,10 @@
 	return 0;
 }
 
-static int mtk_tops_tnl_info_cls_tear_down(struct tops_tnl_info *tnl_info)
+static int mtk_tops_tnl_info_cls_tear_down(struct tops_tnl_info *tnl_info,
+					   struct tops_tnl_params *tnl_params)
 {
-	mtk_tops_tnl_info_cls_entry_unprepare(tnl_info);
+	mtk_tops_tnl_info_cls_entry_unprepare(tnl_info, tnl_params);
 
 	return 0;
 }
@@ -452,6 +483,7 @@
 }
 
 static int mtk_tops_tnl_info_cls_single_setup(struct tops_tnl_info *tnl_info,
+					      struct tops_tnl_params *tnl_params,
 					      struct tops_tnl_type *tnl_type)
 {
 	struct tops_cls_entry *tcls;
@@ -461,20 +493,34 @@
 		return 0;
 
 	if (tnl_info->tcls)
-		return mtk_tops_tnl_info_cls_entry_write(tnl_info);
+		goto cls_entry_write;
 
-	tcls = mtk_tops_tnl_info_cls_entry_prepare(tnl_info);
+	tcls = mtk_tops_tnl_info_cls_entry_prepare(tnl_info, tnl_params);
 	if (IS_ERR(tcls))
 		return PTR_ERR(tcls);
 
-	ret = tnl_type->cls_entry_setup(tnl_info, &tcls->cls->cdesc);
-	if (ret) {
-		TOPS_ERR("tops cls entry setup failed: %d\n", ret);
-		mtk_tops_tnl_info_cls_entry_unprepare(tnl_info);
-		return ret;
+	if (!tnl_params->cdrt) {
+		ret = tnl_type->cls_entry_setup(tnl_info, &tcls->cls->cdesc);
+		if (ret) {
+			TOPS_ERR("tops cls entry setup failed: %d\n", ret);
+			goto cls_entry_unprepare;
+		}
+	} else {
+		/*
+		 * since CLS is already filled up with outer protocol rule
+		 * we only update CLS tport here to let matched packet stop by TOPS
+		 */
+		CLS_DESC_DATA(&tcls->cls->cdesc, tport_idx, 0x7);
 	}
 
+cls_entry_write:
+	ret = mtk_tops_tnl_info_cls_entry_write(tnl_info);
+
+cls_entry_unprepare:
+	if (ret)
+		mtk_tops_tnl_info_cls_entry_unprepare(tnl_info, tnl_params);
+
-	return mtk_tops_tnl_info_cls_entry_write(tnl_info);
+	return ret;
 }
 
 static struct tops_cls_entry *
@@ -490,7 +536,7 @@
 	return NULL;
 }
 
-static bool mtk_tops_tnl_infO_cls_multi_is_updated(struct tops_tnl_info *tnl_info,
+static bool mtk_tops_tnl_info_cls_multi_is_updated(struct tops_tnl_info *tnl_info,
 						   struct tops_tnl_type *tnl_type,
 						   struct cls_desc *cdesc)
 {
@@ -520,19 +566,35 @@
 }
 
 static int mtk_tops_tnl_info_cls_multi_setup(struct tops_tnl_info *tnl_info,
+					     struct tops_tnl_params *tnl_params,
 					     struct tops_tnl_type *tnl_type)
 {
 	struct tops_cls_entry *tcls;
 	struct cls_desc cdesc;
+
 	int ret;
 
-	memset(&cdesc, 0, sizeof(struct cls_desc));
+	if (!tnl_params->cdrt) {
+		memset(&cdesc, 0, sizeof(struct cls_desc));
 
-	/* prepare cls_desc from tnl_type */
-	ret = tnl_type->cls_entry_setup(tnl_info, &cdesc);
-	if (ret) {
-		TOPS_ERR("tops cls entry setup failed: %d\n", ret);
-		return ret;
+		/* prepare cls_desc from tnl_type */
+		ret = tnl_type->cls_entry_setup(tnl_info, &cdesc);
+		if (ret) {
+			TOPS_ERR("tops cls entry setup failed: %d\n", ret);
+			return ret;
+		}
+	} else {
+		struct cdrt_entry *cdrt = mtk_pce_cdrt_entry_find(tnl_params->cdrt);
+
+		if (IS_ERR(cdrt)) {
+			TOPS_ERR("no cdrt idx: %u related CDRT found\n",
+				 tnl_params->cdrt);
+			return ret;
+		}
+
+		memcpy(&cdesc, &cdrt->cls->cdesc, sizeof(struct cls_desc));
+
+		CLS_DESC_DATA(&cdesc, tport_idx, 0x7);
 	}
 
 	/*
@@ -544,7 +606,7 @@
 	 * a tcls is not yet updated or
 	 * tnl_info is not yet associated to a tcls
 	 */
-	if (mtk_tops_tnl_infO_cls_multi_is_updated(tnl_info, tnl_type, &cdesc))
+	if (mtk_tops_tnl_info_cls_multi_is_updated(tnl_info, tnl_type, &cdesc))
 		return 0;
 
 	/* tcls is not yet updated, update this tcls */
@@ -552,16 +614,21 @@
 		return mtk_tops_tnl_info_cls_entry_write(tnl_info);
 
 	/* create a new tcls entry and associate with tnl_info */
-	tcls = mtk_tops_tnl_info_cls_entry_prepare(tnl_info);
+	tcls = mtk_tops_tnl_info_cls_entry_prepare(tnl_info, tnl_params);
 	if (IS_ERR(tcls))
 		return PTR_ERR(tcls);
 
 	memcpy(&tcls->cls->cdesc, &cdesc, sizeof(struct cls_desc));
 
-	return mtk_tops_tnl_info_cls_entry_write(tnl_info);
+	ret = mtk_tops_tnl_info_cls_entry_write(tnl_info);
+	if (ret)
+		mtk_tops_tnl_info_cls_entry_unprepare(tnl_info, tnl_params);
+
+	return ret;
 }
 
-static int mtk_tops_tnl_info_cls_setup(struct tops_tnl_info *tnl_info)
+static int mtk_tops_tnl_info_cls_setup(struct tops_tnl_info *tnl_info,
+				       struct tops_tnl_params *tnl_params)
 {
 	struct tops_tnl_type *tnl_type;
 
@@ -573,9 +640,11 @@
 		return -EINVAL;
 
 	if (!tnl_type->use_multi_cls)
-		return mtk_tops_tnl_info_cls_single_setup(tnl_info, tnl_type);
+		return mtk_tops_tnl_info_cls_single_setup(tnl_info,
+							  tnl_params,
+							  tnl_type);
 
-	return mtk_tops_tnl_info_cls_multi_setup(tnl_info, tnl_type);
+	return mtk_tops_tnl_info_cls_multi_setup(tnl_info, tnl_params, tnl_type);
 }
 
 static int mtk_tops_tnl_info_dipfilter_tear_down(struct tops_tnl_info *tnl_info)
@@ -712,8 +781,11 @@
 
 	lockdep_assert_held(&tnl_info->lock);
 
+	/* manually preserve essential data among encapsulation and decapsulation */
 	tnl_params->flag |= tnl_info->cache.flag;
 	tnl_params->cls_entry = tnl_info->cache.cls_entry;
+	if (tnl_info->cache.cdrt)
+		tnl_params->cdrt = tnl_info->cache.cdrt;
 
 	if (memcmp(&tnl_info->cache, tnl_params, sizeof(struct tops_tnl_params))) {
 		memcpy(&tnl_info->cache, tnl_params, sizeof(struct tops_tnl_params));
@@ -876,6 +948,34 @@
 	return ret;
 }
 
+static int mtk_tops_tnl_l2_update(struct sk_buff *skb)
+{
+	struct tops_tnl_info *tnl_info = skb_to_tnl_info(skb);
+	struct tops_tnl_type *tnl_type;
+	unsigned long flag;
+	int ret;
+
+	if (IS_ERR(tnl_info))
+		return PTR_ERR(tnl_info);
+
+	tnl_type = tnl_info->tnl_type;
+	if (!tnl_type->tnl_l2_param_update)
+		return -ENODEV;
+
+	spin_lock_irqsave(&tnl_info->lock, flag);
+
+	ret = tnl_type->tnl_l2_param_update(skb, &tnl_info->cache);
+	/* tnl params need to be updated */
+	if (ret == 1) {
+		mtk_tops_tnl_info_submit_no_tnl_lock(tnl_info);
+		ret = 0;
+	}
+
+	spin_unlock_irqrestore(&tnl_info->lock, flag);
+
+	return ret;
+}
+
 static bool mtk_tops_tnl_decap_offloadable(struct sk_buff *skb)
 {
 	struct tops_tnl_type *tnl_type;
@@ -962,6 +1062,7 @@
 	}
 
 	tnl_params.tops_entry_proto = tnl_type->tops_entry;
+	tnl_params.cdrt = skb_hnat_cdrt(skb);
 
 	ret = mtk_tops_tnl_offload(skb, tnl_type, &tnl_params);
 
@@ -976,20 +1077,12 @@
 	return ret;
 }
 
-static int mtk_tops_tnl_encap_offload(struct sk_buff *skb)
+static int __mtk_tops_tnl_encap_offload(struct sk_buff *skb)
 {
 	struct tops_tnl_params tnl_params;
 	struct tops_tnl_type *tnl_type;
 	int ret;
 
-	if (unlikely(!mtk_tops_mcu_alive())) {
-		skb_mark_unbind(skb);
-		return -EAGAIN;
-	}
-
-	if (unlikely(!skb_tops_valid(skb) || !skb_hnat_is_encap(skb)))
-		return -EPERM;
-
 	tnl_type = skb_to_tnl_type(skb);
 	if (IS_ERR(tnl_type))
 		return PTR_ERR(tnl_type);
@@ -1003,10 +1096,27 @@
 	if (unlikely(ret))
 		return ret;
 	tnl_params.tops_entry_proto = tnl_type->tops_entry;
+	tnl_params.cdrt = skb_hnat_cdrt(skb);
 
 	return mtk_tops_tnl_offload(skb, tnl_type, &tnl_params);
 }
 
+static int mtk_tops_tnl_encap_offload(struct sk_buff *skb)
+{
+	if (unlikely(!mtk_tops_mcu_alive())) {
+		skb_mark_unbind(skb);
+		return -EAGAIN;
+	}
+
+	if (!skb_hnat_is_encap(skb))
+		return -EPERM;
+
+	if (unlikely(skb_hnat_cdrt(skb)))
+		return mtk_tops_tnl_l2_update(skb);
+
+	return __mtk_tops_tnl_encap_offload(skb);
+}
+
 static struct net_device *mtk_tops_get_tnl_dev(int tnl_idx)
 {
 	if (tnl_idx < TOPS_CRSN_TNL_ID_START || tnl_idx > TOPS_CRSN_TNL_ID_END)
@@ -1136,6 +1246,7 @@
 
 static int mtk_tops_tnl_sync_param_delete(struct tops_tnl_info *tnl_info)
 {
+	struct tops_tnl_params tnl_params;
 	int ret;
 
 	ret = mtk_tops_tnl_info_dipfilter_tear_down(tnl_info);
@@ -1145,13 +1256,14 @@
 		return ret;
 	}
 
+	memcpy(&tnl_params, &tnl_info->tnl_params, sizeof(struct tops_tnl_params));
 	ret = __mtk_tops_tnl_sync_param_delete(tnl_info);
 	if (ret) {
 		TOPS_ERR("tnl sync deletion failed: %d\n", ret);
 		return ret;
 	}
 
-	ret = mtk_tops_tnl_info_cls_tear_down(tnl_info);
+	ret = mtk_tops_tnl_info_cls_tear_down(tnl_info, &tnl_params);
 	if (ret) {
 		TOPS_ERR("tnl sync cls tear down faild: %d\n",
 			 ret);
@@ -1200,7 +1312,7 @@
 	int ret;
 
 	if (setup_pce) {
-		ret = mtk_tops_tnl_info_cls_setup(tnl_info);
+		ret = mtk_tops_tnl_info_cls_setup(tnl_info, &tnl_info->tnl_params);
 		if (ret) {
 			TOPS_ERR("tnl cls setup failed: %d\n", ret);
 			return ret;
@@ -1227,7 +1339,7 @@
 	return ret;
 
 cls_tear_down:
-	mtk_tops_tnl_info_cls_tear_down(tnl_info);
+	mtk_tops_tnl_info_cls_tear_down(tnl_info, &tnl_info->tnl_params);
 
 	return ret;
 }
@@ -1532,7 +1644,7 @@
 
 		mtk_tops_tnl_info_dipfilter_tear_down(tnl_info);
 
-		mtk_tops_tnl_info_cls_tear_down(tnl_info);
+		mtk_tops_tnl_info_cls_tear_down(tnl_info, &tnl_info->tnl_params);
 	}
 
 	spin_unlock_irqrestore(&tops_tnl.tbl_lock, flag);