[][Add QDMA Per-Port-Per-Queue(PPPQ) mode]

[Description]
Add Panther QDMA Per-Port-Per-Queue(PPPQ) mode for solving
uplink pause frame issue.

After enabling PPPQ mode, each LAN port will be mapped into
a specified QDMA queue.

[Usage]
- echo 2 > /sys/kernel/debug/hnat/qos_toggle	(PPPQ Mode)
- echo 1 > /sys/kernel/debug/hnat/qos_toggle	(HQoS Mode)
- echo 0 > /sys/kernel/debug/hnat/qos_toggle	(Disable QDMA)

[Release-log]
N/A

Change-Id: Ic554bce717d6384cbb31a4c44cdc7988e82719e7
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5288809
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
index 5c7d67d..5646a3a 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
@@ -745,7 +745,7 @@
 	}
 
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
-	if (IS_GMAC1_MODE)
+	if (qos_toggle && IS_GMAC1_MODE)
 		dev_add_pack(&mtk_pack_type);
 #endif
 	err = hnat_roaming_enable();
@@ -789,7 +789,7 @@
 		del_timer_sync(&hnat_priv->hnat_reset_timestamp_timer);
 
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
-	if (IS_GMAC1_MODE)
+	if (qos_toggle && IS_GMAC1_MODE)
 		dev_remove_pack(&mtk_pack_type);
 #endif
 
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
index 52c8419..19482e5 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
@@ -845,6 +845,8 @@
 	 IS_IPV4_DSLITE(x) | IS_IPV4_MAPE(x) | IS_IPV4_MAPT(x))
 #define IS_BOND_MODE (!strncmp(LAN_DEV_NAME, "bond", 4))
 #define IS_GMAC1_MODE ((hnat_priv->gmac_num == 1) ? 1 : 0)
+#define IS_HQOS_MODE (qos_toggle == 1)
+#define IS_PPPQ_MODE (qos_toggle == 2)		/* Per Port Per Queue */
 
 #define es(entry) (entry_state[entry->bfib1.state])
 #define ei(entry, end) (hnat_priv->foe_etry_num - (int)(end - entry))
@@ -886,20 +888,20 @@
 extern struct mtk_hnat *hnat_priv;
 
 #if defined(CONFIG_NET_DSA_MT7530)
-void hnat_dsa_fill_stag(const struct net_device *netdev,
-			struct foe_entry *entry,
-			struct flow_offload_hw_path *hw_path,
-			u16 eth_proto, int mape);
+u32 hnat_dsa_fill_stag(const struct net_device *netdev,
+		       struct foe_entry *entry,
+		       struct flow_offload_hw_path *hw_path,
+		       u16 eth_proto, int mape);
 
 static inline bool hnat_dsa_is_enable(struct mtk_hnat *priv)
 {
 	return (priv->wan_dsa_port != NONE_DSA_PORT);
 }
 #else
-static inline void hnat_dsa_fill_stag(const struct net_device *netdev,
-				      struct foe_entry *entry,
-				      struct flow_offload_hw_path *hw_path,
-				      u16 eth_proto, int mape)
+static inline u32 hnat_dsa_fill_stag(const struct net_device *netdev,
+				     struct foe_entry *entry,
+				     struct flow_offload_hw_path *hw_path,
+				     u16 eth_proto, int mape)
 {
 }
 
@@ -920,6 +922,7 @@
 extern int debug_level;
 extern int hook_toggle;
 extern int mape_toggle;
+extern int qos_toggle;
 
 int ext_if_add(struct extdev_entry *ext_entry);
 int ext_if_del(struct extdev_entry *ext_entry);
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
index 71df49c..4b9006a 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
@@ -27,6 +27,7 @@
 int dbg_cpu_reason;
 int hook_toggle;
 int mape_toggle;
+int qos_toggle;
 unsigned int dbg_cpu_reason_cnt[MAX_CRSN_NUM];
 
 static const char * const entry_state[] = { "INVALID", "UNBIND", "BIND", "FIN" };
@@ -1957,6 +1958,49 @@
 	.release = single_release,
 };
 
+static int hnat_qos_toggle_read(struct seq_file *m, void *private)
+{
+	pr_info("value=%d, HQoS is %s now!\n", qos_toggle, (qos_toggle) ? "enabled" : "disabled");
+
+	return 0;
+}
+
+static int hnat_qos_toggle_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, hnat_qos_toggle_read, file->private_data);
+}
+
+static ssize_t hnat_qos_toggle_write(struct file *file, const char __user *buffer,
+				     size_t count, loff_t *data)
+{
+	char buf[8];
+	int len = count;
+
+	if ((len > 8) || copy_from_user(buf, buffer, len))
+		return -EFAULT;
+
+	if (buf[0] == '0' && qos_toggle != 0) {
+		pr_info("HQoS is going to be disabled !\n");
+		qos_toggle = 0;
+	} else if (buf[0] == '1' && qos_toggle != 1) {
+		pr_info("HQoS mode is going to be enabled !\n");
+		qos_toggle = 1;
+	} else if (buf[0] == '2' && qos_toggle != 2) {
+		pr_info("Per-port-per-queue mode is going to be enabled !\n");
+		qos_toggle = 2;
+	}
+
+	return len;
+}
+
+static const struct file_operations hnat_qos_toggle_fops = {
+	.open = hnat_qos_toggle_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.write = hnat_qos_toggle_write,
+	.release = single_release,
+};
+
 static int hnat_version_read(struct seq_file *m, void *private)
 {
 	pr_info("HNAT SW version : %s\nHNAT HW version : %d\n", HNAT_SW_VER, hnat_priv->data->version);
@@ -2119,6 +2163,8 @@
 			    &hnat_hook_toggle_fops);
 	debugfs_create_file("mape_toggle", S_IRUGO | S_IRUGO, root, h,
 			    &hnat_mape_toggle_fops);
+	debugfs_create_file("qos_toggle", S_IRUGO | S_IRUGO, root, h,
+			    &hnat_qos_toggle_fops);
 	debugfs_create_file("hnat_version", S_IRUGO | S_IRUGO, root, h,
 			    &hnat_version_fops);
 	debugfs_create_file("hnat_ppd_if", S_IRUGO | S_IRUGO, root, h,
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
index c76189d..c36ee98 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
@@ -475,7 +475,7 @@
 	skb->dev = get_dev_from_index(index);
 
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
-	if (eth_hdr(skb)->h_proto == HQOS_MAGIC_TAG) {
+	if (qos_toggle && eth_hdr(skb)->h_proto == HQOS_MAGIC_TAG) {
 		skb = skb_unshare(skb, GFP_ATOMIC);
 		if (!skb)
 			return NF_ACCEPT;
@@ -802,7 +802,7 @@
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
 	struct vlan_ethhdr *veth;
 
-	if (hnat_priv->data->whnat) {
+	if (qos_toggle && hnat_priv->data->whnat) {
 		veth = (struct vlan_ethhdr *)skb_mac_header(skb);
 
 		if (eth_hdr(skb)->h_proto == HQOS_MAGIC_TAG) {
@@ -846,7 +846,8 @@
 
 		/* packets from external devices -> xxx ,step 2, learning stage */
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
-		if (do_ext2ge_fast_learn(state->in, skb) && (eth_hdr(skb)->h_proto != HQOS_MAGIC_TAG)) {
+		if (do_ext2ge_fast_learn(state->in, skb) && (!qos_toggle ||
+		    (qos_toggle && eth_hdr(skb)->h_proto != HQOS_MAGIC_TAG))) {
 #else
 		if (do_ext2ge_fast_learn(state->in, skb)) {
 #endif
@@ -1094,6 +1095,7 @@
 	u32 gmac = NR_DISCARD;
 	int udp = 0;
 	u32 qid = 0;
+	u32 port_id = 0;
 	int mape = 0;
 
 	if (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPIP)
@@ -1352,10 +1354,12 @@
 				entry.ipv4_hnapt.etype = htons(ETH_P_IP);
 
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
-				entry.ipv4_hnapt.iblk2.qid =
-					(hnat_priv->data->version == MTK_HNAT_V4) ?
-					 skb->mark & 0x7f : skb->mark & 0xf;
-				entry.ipv4_hnapt.iblk2.fqos = 1;
+				if (qos_toggle) {
+					entry.ipv4_hnapt.iblk2.qid =
+						(hnat_priv->data->version == MTK_HNAT_V4) ?
+						 skb->mark & 0x7f : skb->mark & 0xf;
+					entry.ipv4_hnapt.iblk2.fqos = 1;
+				}
 #endif
 
 				entry.ipv4_hnapt.bfib1.udp =
@@ -1421,8 +1425,9 @@
 
 	if (IS_LAN(dev)) {
 		if (IS_DSA_LAN(dev))
-			hnat_dsa_fill_stag(dev, &entry, hw_path,
-					   ntohs(eth->h_proto), mape);
+			port_id = hnat_dsa_fill_stag(dev, &entry, hw_path,
+						     ntohs(eth->h_proto),
+						     mape);
 
 		if (IS_BOND_MODE)
 			gmac = ((skb_hnat_entry(skb) >> 1) % hnat_priv->gmac_num) ?
@@ -1431,8 +1436,9 @@
 			gmac = NR_GMAC1_PORT;
 	} else if (IS_WAN(dev)) {
 		if (IS_DSA_WAN(dev))
-			hnat_dsa_fill_stag(dev, &entry, hw_path,
-					   ntohs(eth->h_proto), mape);
+			port_id = hnat_dsa_fill_stag(dev,&entry, hw_path,
+						     ntohs(eth->h_proto),
+						     mape);
 		if (mape_toggle && mape == 1) {
 			gmac = NR_PDMA_PORT;
 			/* Set act_dp = wan_dev */
@@ -1470,36 +1476,45 @@
 		return 0;
 	}
 
-	qid = skb->mark & (MTK_QDMA_TX_MASK);
+	if (IS_HQOS_MODE)
+		qid = skb->mark & (MTK_QDMA_TX_MASK);
+	else if (IS_PPPQ_MODE)
+		qid = port_id & MTK_QDMA_TX_MASK;
+	else
+		qid = 0;
 
 	if (IS_IPV4_GRP(foe)) {
 		entry.ipv4_hnapt.iblk2.dp = gmac;
 		entry.ipv4_hnapt.iblk2.port_mg =
 			(hnat_priv->data->version == MTK_HNAT_V1) ? 0x3f : 0;
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
-		if (hnat_priv->data->version == MTK_HNAT_V4) {
-			entry.ipv4_hnapt.iblk2.qid = qid & 0x7f;
-		} else {
-			/* qid[5:0]= port_mg[1:0]+ qid[3:0] */
-			entry.ipv4_hnapt.iblk2.qid = qid & 0xf;
-			if (hnat_priv->data->version != MTK_HNAT_V1)
-				entry.ipv4_hnapt.iblk2.port_mg |=
-					((qid >> 4) & 0x3);
+		if (qos_toggle) {
+			if (hnat_priv->data->version == MTK_HNAT_V4) {
+				entry.ipv4_hnapt.iblk2.qid = qid & 0x7f;
+			} else {
+				/* qid[5:0]= port_mg[1:0]+ qid[3:0] */
+				entry.ipv4_hnapt.iblk2.qid = qid & 0xf;
+				if (hnat_priv->data->version != MTK_HNAT_V1)
+					entry.ipv4_hnapt.iblk2.port_mg |=
+						((qid >> 4) & 0x3);
 
-			if (((IS_EXT(dev) && (FROM_GE_LAN(skb) ||
-			      FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb))) ||
-			      ((mape_toggle && mape == 1) && !FROM_EXT(skb))) &&
-			      (!whnat)) {
-				entry.ipv4_hnapt.etype = htons(HQOS_MAGIC_TAG);
-				entry.ipv4_hnapt.vlan1 = skb_hnat_entry(skb);
-				entry.bfib1.vlan_layer = 1;
+				if (((IS_EXT(dev) && (FROM_GE_LAN(skb) ||
+				      FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb))) ||
+				      ((mape_toggle && mape == 1) && !FROM_EXT(skb))) &&
+				      (!whnat)) {
+					entry.ipv4_hnapt.etype = htons(HQOS_MAGIC_TAG);
+					entry.ipv4_hnapt.vlan1 = skb_hnat_entry(skb);
+					entry.bfib1.vlan_layer = 1;
+				}
 			}
-		}
 
-		if (FROM_EXT(skb) || skb_hnat_sport(skb) == NR_QDMA_PORT)
+			if (FROM_EXT(skb) || skb_hnat_sport(skb) == NR_QDMA_PORT)
+				entry.ipv4_hnapt.iblk2.fqos = 0;
+			else
+				entry.ipv4_hnapt.iblk2.fqos = 1;
+		} else {
 			entry.ipv4_hnapt.iblk2.fqos = 0;
-		else
-			entry.ipv4_hnapt.iblk2.fqos = 1;
+		}
 #else
 		entry.ipv4_hnapt.iblk2.fqos = 0;
 #endif
@@ -1508,28 +1523,32 @@
 		entry.ipv6_5t_route.iblk2.port_mg =
 			(hnat_priv->data->version == MTK_HNAT_V1) ? 0x3f : 0;
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
-		if (hnat_priv->data->version == MTK_HNAT_V4) {
-			entry.ipv6_5t_route.iblk2.qid = qid & 0x7f;
-		} else {
-			/* qid[5:0]= port_mg[1:0]+ qid[3:0] */
-			entry.ipv6_5t_route.iblk2.qid = qid & 0xf;
-			if (hnat_priv->data->version != MTK_HNAT_V1)
-				entry.ipv6_5t_route.iblk2.port_mg |=
-							((qid >> 4) & 0x3);
+		if (qos_toggle) {
+			if (hnat_priv->data->version == MTK_HNAT_V4) {
+				entry.ipv6_5t_route.iblk2.qid = qid & 0x7f;
+			} else {
+				/* qid[5:0]= port_mg[1:0]+ qid[3:0] */
+				entry.ipv6_5t_route.iblk2.qid = qid & 0xf;
+				if (hnat_priv->data->version != MTK_HNAT_V1)
+					entry.ipv6_5t_route.iblk2.port_mg |=
+								((qid >> 4) & 0x3);
 
-			if (IS_EXT(dev) && (FROM_GE_LAN(skb) ||
-			    FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb)) &&
-			    (!whnat)) {
-				entry.ipv6_5t_route.etype = htons(HQOS_MAGIC_TAG);
-				entry.ipv6_5t_route.vlan1 = skb_hnat_entry(skb);
-				entry.bfib1.vlan_layer = 1;
+				if (IS_EXT(dev) && (FROM_GE_LAN(skb) ||
+				    FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb)) &&
+				    (!whnat)) {
+					entry.ipv6_5t_route.etype = htons(HQOS_MAGIC_TAG);
+					entry.ipv6_5t_route.vlan1 = skb_hnat_entry(skb);
+					entry.bfib1.vlan_layer = 1;
+				}
 			}
-		}
 
-		if (FROM_EXT(skb))
+			if (FROM_EXT(skb))
+				entry.ipv6_5t_route.iblk2.fqos = 0;
+			else
+				entry.ipv6_5t_route.iblk2.fqos = 1;
+		} else {
 			entry.ipv6_5t_route.iblk2.fqos = 0;
-		else
-			entry.ipv6_5t_route.iblk2.fqos = 1;
+		}
 #else
 		entry.ipv6_5t_route.iblk2.fqos = 0;
 #endif
@@ -1650,6 +1669,10 @@
 			entry->ipv4_hnapt.winfo.bssid = skb_hnat_bss_id(skb);
 			entry->ipv4_hnapt.winfo.wcid = skb_hnat_wc_id(skb);
 #if defined(CONFIG_MEDIATEK_NETSYS_V2)
+#if defined(CONFIG_NET_MEDIATEK_HW_QOS)
+			if (qos_toggle)
+				entry->ipv4_hnapt.iblk2.fqos = 1;
+#endif
 			entry->ipv4_hnapt.iblk2.rxid = skb_hnat_rx_id(skb);
 			entry->ipv4_hnapt.iblk2.winfoi = 1;
 #else
@@ -1669,7 +1692,8 @@
 			}
 
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
-			if (FROM_GE_LAN(skb) || FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb)) {
+			if (qos_toggle &&
+			    (FROM_GE_LAN(skb) || FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb))) {
 				entry->bfib1.vpm = 0;
 				entry->bfib1.vlan_layer = 1;
 				entry->ipv4_hnapt.etype = htons(HQOS_MAGIC_TAG);
@@ -1688,6 +1712,10 @@
 			entry->ipv6_5t_route.winfo.bssid = skb_hnat_bss_id(skb);
 			entry->ipv6_5t_route.winfo.wcid = skb_hnat_wc_id(skb);
 #if defined(CONFIG_MEDIATEK_NETSYS_V2)
+#if defined(CONFIG_NET_MEDIATEK_HW_QOS)
+			if (qos_toggle)
+				entry->ipv6_5t_route.iblk2.fqos = 1;
+#endif
 			entry->ipv6_5t_route.iblk2.rxid = skb_hnat_rx_id(skb);
 			entry->ipv6_5t_route.iblk2.winfoi = 1;
 #else
@@ -1707,7 +1735,8 @@
 			}
 
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
-			if (FROM_GE_LAN(skb) || FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb)) {
+			if (qos_toggle &&
+			    (FROM_GE_LAN(skb) || FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb))) {
 				entry->bfib1.vpm = 0;
 				entry->bfib1.vlan_layer = 1;
 				entry->ipv6_5t_route.etype = htons(HQOS_MAGIC_TAG);
@@ -2063,7 +2092,7 @@
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
 	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
 
-	if (eth_hdr(skb)->h_proto == HQOS_MAGIC_TAG) {
+	if (qos_toggle && eth_hdr(skb)->h_proto == HQOS_MAGIC_TAG) {
 		skb_hnat_entry(skb) = ntohs(veth->h_vlan_TCI) & 0x3fff;
 		skb_hnat_reason(skb) = HIT_BIND_FORCE_TO_CPU;
 	}
@@ -2074,7 +2103,8 @@
 
 	/* packets from external devices -> xxx ,step 2, learning stage */
 #if defined(CONFIG_NET_MEDIATEK_HW_QOS)
-	if (do_ext2ge_fast_learn(state->in, skb) && (eth_hdr(skb)->h_proto != HQOS_MAGIC_TAG)) {
+	if (do_ext2ge_fast_learn(state->in, skb) && (!qos_toggle ||
+	    (qos_toggle && eth_hdr(skb)->h_proto != HQOS_MAGIC_TAG))) {
 #else
 	if (do_ext2ge_fast_learn(state->in, skb)) {
 #endif
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
index b0fabfb..aa40d99 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
@@ -8,11 +8,11 @@
 #include <net/netfilter/nf_flow_table.h>
 #include "hnat.h"
 
-void hnat_dsa_fill_stag(const struct net_device *netdev,
-			struct foe_entry *entry,
-			struct flow_offload_hw_path *hw_path,
-			u16 eth_proto,
-			int mape)
+u32 hnat_dsa_fill_stag(const struct net_device *netdev,
+		       struct foe_entry *entry,
+		       struct flow_offload_hw_path *hw_path,
+		       u16 eth_proto,
+		       int mape)
 {
 	const struct net_device *ndev;
 	const unsigned int *port_reg;
@@ -54,4 +54,6 @@
 	default:
 		pr_info("DSA + HNAT unsupport protocol\n");
 	}
+
+	return port_index;
 }
diff --git a/target/linux/mediatek/modules.mk b/target/linux/mediatek/modules.mk
index 67a983a..2ce0201 100644
--- a/target/linux/mediatek/modules.mk
+++ b/target/linux/mediatek/modules.mk
@@ -70,7 +70,7 @@
 	CONFIG_BRIDGE_NETFILTER=y \
 	CONFIG_NETFILTER_FAMILY_BRIDGE=y \
 	CONFIG_NET_MEDIATEK_HNAT \
-	CONFIG_NET_MEDIATEK_HW_QOS=n
+	CONFIG_NET_MEDIATEK_HW_QOS=y
   FILES:= \
         $(LINUX_DIR)/drivers/net/ethernet/mediatek/mtk_hnat/mtkhnat.ko
 endef