[qca-nss-sfe] 802.1Q/802.1ad VLAN support in SFE
Change-Id: I8007484b229b0dac0aff6dc284641b94090539db
Signed-off-by: Wayne Tan <quic_wtan@quicinc.com>
diff --git a/sfe_vlan.h b/sfe_vlan.h
new file mode 100644
index 0000000..505b24b
--- /dev/null
+++ b/sfe_vlan.h
@@ -0,0 +1,217 @@
+/*
+ * sfe_vlan.h
+ * Shortcut flow acceleration for 802.1AD/802.1Q flow
+ *
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __SFE_VLAN_H
+#define __SFE_VLAN_H
+
+#include <linux/if_vlan.h>
+
+/*
+ * sfe_vlan_check_and_parse_tag()
+ *
+ * case 1: QinQ frame (e.g. outer tag = 88a80032, inner tag = 81000001):
+ * When entering this function:
+ * ----+-----------------+-----|-----+-----------+-----+---------
+ * |DMAC |SMAC |88|a8|00|32|81|00|00|01|08|00|45|00|
+ * ----+-----------------+-----A-----+-----------+-----+---------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_8021AD)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * When exiting:
+ * ----+-----------------+-----------+-----------+-----+---------
+ * |DMAC |SMAC |88|a8|00|32|81|00|00|01|08|00|45|00|
+ * ----+-----------------+-----------+-----------+-----A---------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_IP)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * l2_info->vlan_hdr_cnt = 2
+ * l2_info->vlan_hdr[0].tpid = ntohs(ETH_P_8021AD)
+ * l2_info->vlan_hdr[0].tci = 0x0032
+ * l2_info->vlan_hdr[1].tpid = ntohs(ETH_P_8021Q)
+ * l2_info->vlan_hdr[1].tci = 0x0001
+ * l2_info->protocol = ETH_P_IP
+ *
+ * case 2: 802.1Q frame (e.g. the tag is 81000001):
+ * When entering this function:
+ * ----+-----------------+-----|-----+-----+---------
+ * |DMAC |SMAC |81|00|00|01|08|00|45|00|
+ * ----+-----------------+-----A-----+-----+---------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_8021Q)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * When exiting:
+ * ----+-----------------+-----------+-----+---------
+ * |DMAC |SMAC |81|00|00|01|08|00|45|00|
+ * ----+-----------------+-----------+-----A---------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_IP)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * l2_info->vlan_hdr_cnt = 1
+ * l2_info->vlan_hdr[0].tpid = ntohs(ETH_P_8021Q)
+ * l2_info->vlan_hdr[0].tci = 0x0001
+ * l2_info->protocol = ETH_P_IP
+ *
+ * case 3: untagged frame
+ * When entering this function:
+ * ----+-----------------+-----|---------------------
+ * |DMAC |SMAC |08|00|45|00|
+ * ----+-----------------+-----A---------------------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_IP)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * When exiting:
+ * ----+-----------------+-----|---------------------
+ * |DMAC |SMAC |08|00|45|00|
+ * ----+-----------------+-----A---------------------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_IP)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * l2_info->vlan_hdr_cnt = 0
+ * l2_info->protocol = ETH_P_IP
+ */
+static inline bool sfe_vlan_check_and_parse_tag(struct sk_buff *skb, struct sfe_l2_info *l2_info)
+{
+ struct vlan_hdr *vhdr;
+
+ l2_info->vlan_hdr_cnt = 0;
+
+ while ((skb->protocol == htons(ETH_P_8021AD) || skb->protocol == htons(ETH_P_8021Q)) &&
+ l2_info->vlan_hdr_cnt < SFE_MAX_VLAN_DEPTH) {
+ if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) {
+ return false;
+ }
+ vhdr = (struct vlan_hdr *)skb->data;
+ l2_info->vlan_hdr[l2_info->vlan_hdr_cnt].tpid = skb->protocol;
+ l2_info->vlan_hdr[l2_info->vlan_hdr_cnt].tci = ntohs(vhdr->h_vlan_TCI);
+ skb->protocol = vhdr->h_vlan_encapsulated_proto;
+ l2_info->vlan_hdr_cnt++;
+ __skb_pull(skb, VLAN_HLEN);
+ }
+
+ l2_info->protocol = htons(skb->protocol);
+ return true;
+}
+
+/*
+ * sfe_vlan_undo_parse()
+ * Restore some skb fields which are modified when parsing VLAN tags.
+ */
+static inline void sfe_vlan_undo_parse(struct sk_buff *skb, struct sfe_l2_info *l2_info)
+{
+ if (l2_info->vlan_hdr_cnt == 0) {
+ return;
+ }
+
+ skb->protocol = l2_info->vlan_hdr[0].tpid;
+ __skb_push(skb, l2_info->vlan_hdr_cnt * VLAN_HLEN);
+}
+
+/*
+ * sfe_vlan_validate_ingress_tag()
+ * Validate ingress packet's VLAN tag
+ */
+static inline bool sfe_vlan_validate_ingress_tag(
+ struct sk_buff *skb, u8 count, struct sfe_vlan_hdr *vlan_hdr, struct sfe_l2_info *l2_info)
+{
+ u8 i;
+
+ if (likely(!sfe_is_l2_feature_enabled())) {
+ return true;
+ }
+
+ if (unlikely(count != l2_info->vlan_hdr_cnt)) {
+ return false;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (unlikely(vlan_hdr[i].tpid != l2_info->vlan_hdr[i].tpid)) {
+ return false;
+ }
+
+ if (unlikely((vlan_hdr[i].tci & VLAN_VID_MASK) !=
+ (l2_info->vlan_hdr[i].tci & VLAN_VID_MASK))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * sfe_vlan_add_tag()
+ * Add VLAN tags at skb->data.
+ * Normally, it is called just before adding 14-byte Ethernet header.
+ *
+ * This function does not update skb->mac_header so later code
+ * needs to call skb_reset_mac_header()/skb_reset_mac_len() to
+ * get correct skb->mac_header/skb->mac_len.
+ *
+ * It assumes:
+ * - skb->protocol is set
+ * - skb has enough headroom to write VLAN tags
+ * - 0 < count <= SFE_MAX_VLAN_DEPTH
+ *
+ * When entering (e.g. skb->protocol = ntohs(ETH_P_IP) or ntohs(ETH_P_PPP_SES)):
+ * -------------------------------+---------------------
+ * |45|00|...
+ * -------------------------------A---------------------
+ * skb->data
+ * -------------------------------v-----------------+-----+----------
+ * |11|00|xx|xx|xx|xx|00|21|45|00|...
+ * -------------------------------+-----------------+-----+----------
+ *
+ * When exiting (e.g. to add outer/inner tag = 88a80032/81000001):
+ * -------------+-----------+-----+---------------------
+ * |00|32|81|00|00|01|08|00|45|00|05|d8|....
+ * -------A-----+-----------+-----+---------------------
+ * skb->data
+ * -------v-----+-----------+-----+-----------------+-----+----------
+ * |00|32|81|00|00|01|88|64|11|00|xx|xx|xx|xx|00|21|45|00|
+ * -------------+-----------+-----+-----------------+-----+----------
+ * skb->protocol = ntohs(ETH_P_8021AD)
+ */
+static inline void sfe_vlan_add_tag(struct sk_buff *skb, int count, struct sfe_vlan_hdr *vlan)
+{
+ struct vlan_hdr *vhdr;
+ int i;
+ vlan += (count - 1);
+
+ for (i = 0; i < count; i++) {
+ skb_push(skb, VLAN_HLEN);
+ vhdr = (struct vlan_hdr *)skb->data;
+ vhdr->h_vlan_TCI = htons(vlan->tci);
+ vhdr->h_vlan_encapsulated_proto = skb->protocol;
+ skb->protocol = vlan->tpid;
+ vlan--;
+ }
+}
+
+#endif /* __SFE_VLAN_H */