[ovs-dev] [PATCH 4/4] Add support to offload QinQ double VLAN headers match
Jianbo Liu
jianbol at mellanox.com
Mon Jul 16 10:00:03 UTC 2018
Currently the inner VLAN header is ignored when using the TC data-path.
As TC flower supports QinQ, now we can offload the rules to match on both
outer and inner VLAN headers.
Signed-off-by: Jianbo Liu <jianbol at mellanox.com>
Reviewed-by: Roi Dayan <roid at mellanox.com>
---
acinclude.m4 | 6 ++---
include/linux/pkt_cls.h | 6 ++++-
lib/dpif-netlink.c | 2 +-
lib/netdev-tc-offloads.c | 57 ++++++++++++++++++++++++++++++++++++--------
lib/tc.c | 62 +++++++++++++++++++++++++++++++++++++++++-------
lib/tc.h | 7 +++---
6 files changed, 113 insertions(+), 27 deletions(-)
diff --git a/acinclude.m4 b/acinclude.m4
index 991a627..d6e0d33 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -178,10 +178,10 @@ dnl Configure Linux tc compat.
AC_DEFUN([OVS_CHECK_LINUX_TC], [
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([#include <linux/pkt_cls.h>], [
- int x = TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST;
+ int x = TCA_FLOWER_KEY_CVLAN_PRIO;
])],
- [AC_DEFINE([HAVE_TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST], [1],
- [Define to 1 if TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST is avaiable.])])
+ [AC_DEFINE([HAVE_TCA_FLOWER_KEY_CVLAN_PRIO], [1],
+ [Define to 1 if TCA_FLOWER_KEY_CVLAN_PRIO is avaiable.])])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([#include <linux/tc_act/tc_vlan.h>], [
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
index 60976f3..442323d 100644
--- a/include/linux/pkt_cls.h
+++ b/include/linux/pkt_cls.h
@@ -1,7 +1,7 @@
#ifndef __LINUX_PKT_CLS_WRAPPER_H
#define __LINUX_PKT_CLS_WRAPPER_H 1
-#if defined(__KERNEL__) || defined(HAVE_TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST)
+#if defined(__KERNEL__) || defined(HAVE_TCA_FLOWER_KEY_CVLAN_PRIO)
#include_next <linux/pkt_cls.h>
#else
@@ -196,6 +196,10 @@ enum {
TCA_FLOWER_KEY_IP_TTL, /* u8 */
TCA_FLOWER_KEY_IP_TTL_MASK, /* u8 */
+ TCA_FLOWER_KEY_CVLAN_ID, /* be16 */
+ TCA_FLOWER_KEY_CVLAN_PRIO, /* u8 */
+ TCA_FLOWER_KEY_CVLAN_ETH_TYPE, /* be16 */
+
__TCA_FLOWER_MAX,
};
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index aa9bbd9..62c7120 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -1709,7 +1709,7 @@ dpif_netlink_netdev_match_to_dpif_flow(struct match *match,
.flow = &match->flow,
.mask = &match->wc.masks,
.support = {
- .max_vlan_headers = 1,
+ .max_vlan_headers = 2,
},
};
size_t offset;
diff --git a/lib/netdev-tc-offloads.c b/lib/netdev-tc-offloads.c
index 3405458..272e86e 100644
--- a/lib/netdev-tc-offloads.c
+++ b/lib/netdev-tc-offloads.c
@@ -431,9 +431,20 @@ parse_tc_flower_to_match(struct tc_flower *flower,
match_set_dl_dst_masked(match, key->dst_mac, mask->dst_mac);
if (eth_type_vlan(key->eth_type)) {
- match_set_dl_vlan(match, htons(key->vlan_id));
- match_set_dl_vlan_pcp(match, key->vlan_prio, 0);
- match_set_dl_type(match, key->encap_eth_type, 0);
+ match->flow.vlans[0].tpid = key->eth_type;
+ match->wc.masks.vlans[0].tpid = OVS_BE16_MAX;
+ match_set_dl_vlan(match, htons(key->vlan_id[0]), 0);
+ match_set_dl_vlan_pcp(match, key->vlan_prio[0], 0);
+
+ if (eth_type_vlan(key->encap_eth_type[0])) {
+ match_set_dl_vlan(match, htons(key->vlan_id[1]), 1);
+ match_set_dl_vlan_pcp(match, key->vlan_prio[1], 1);
+ match_set_dl_type(match, key->encap_eth_type[1]);
+ match->flow.vlans[1].tpid = key->encap_eth_type[0];
+ match->wc.masks.vlans[1].tpid = OVS_BE16_MAX;
+ } else {
+ match_set_dl_type(match, key->encap_eth_type[0]);
+ }
flow_fix_vlan_tpid(&match->flow);
} else {
match_set_dl_type(match, key->eth_type);
@@ -954,14 +965,14 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
&& (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK))
&& (vid_mask || pcp_mask)) {
if (vid_mask) {
- flower.key.vlan_id = vlan_tci_to_vid(key->vlans[0].tci);
- VLOG_DBG_RL(&rl, "vlan_id: %d\n", flower.key.vlan_id);
+ flower.key.vlan_id[0] = vlan_tci_to_vid(key->vlans[0].tci);
+ VLOG_DBG_RL(&rl, "vlan_id[0]: %d\n", flower.key.vlan_id[0]);
}
if (pcp_mask) {
- flower.key.vlan_prio = vlan_tci_to_pcp(key->vlans[0].tci);
- VLOG_DBG_RL(&rl, "vlan_prio: %d\n", flower.key.vlan_prio);
+ flower.key.vlan_prio[0] = vlan_tci_to_pcp(key->vlans[0].tci);
+ VLOG_DBG_RL(&rl, "vlan_prio[0]: %d\n", flower.key.vlan_prio[0]);
}
- flower.key.encap_eth_type = flower.key.eth_type;
+ flower.key.encap_eth_type[0] = flower.key.eth_type;
flower.key.eth_type = key->vlans[0].tpid;
} else if (mask->vlans[0].tci == htons(0xffff) &&
ntohs(key->vlans[0].tci) == 0) {
@@ -970,8 +981,34 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
/* partial mask */
return EOPNOTSUPP;
}
- } else if (mask->vlans[1].tci) {
- return EOPNOTSUPP;
+ }
+
+ if (mask->vlans[1].tci) {
+ ovs_be16 vid_mask = mask->vlans[1].tci & htons(VLAN_VID_MASK);
+ ovs_be16 pcp_mask = mask->vlans[1].tci & htons(VLAN_PCP_MASK);
+ ovs_be16 cfi = mask->vlans[1].tci & htons(VLAN_CFI);
+
+ if (cfi && key->vlans[1].tci & htons(VLAN_CFI)
+ && (!vid_mask || vid_mask == htons(VLAN_VID_MASK))
+ && (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK))
+ && (vid_mask || pcp_mask)) {
+ if (vid_mask) {
+ flower.key.vlan_id[1] = vlan_tci_to_vid(key->vlans[1].tci);
+ VLOG_DBG_RL(&rl, "vlan_id[1]: %d", flower.key.vlan_id[1]);
+ }
+ if (pcp_mask) {
+ flower.key.vlan_prio[1] = vlan_tci_to_pcp(key->vlans[1].tci);
+ VLOG_DBG_RL(&rl, "vlan_prio[1]: %d", flower.key.vlan_prio[1]);
+ }
+ flower.key.encap_eth_type[1] = flower.key.encap_eth_type[0];
+ flower.key.encap_eth_type[0] = key->vlans[1].tpid;
+ } else if (mask->vlans[1].tci == htons(0xffff) &&
+ ntohs(key->vlans[1].tci) == 0) {
+ /* exact && no vlan */
+ } else {
+ /* partial mask */
+ return EOPNOTSUPP;
+ }
}
memset(mask->vlans, 0, sizeof mask->vlans);
diff --git a/lib/tc.c b/lib/tc.c
index 5030399..2157135 100644
--- a/lib/tc.c
+++ b/lib/tc.c
@@ -306,6 +306,9 @@ static const struct nl_policy tca_flower_policy[] = {
.optional = true, },
[TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NL_A_U16,
.optional = true, },
+ [TCA_FLOWER_KEY_CVLAN_ID] = { .type = NL_A_U16, .optional = true, },
+ [TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NL_A_U8, .optional = true, },
+ [TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, },
};
static void
@@ -332,21 +335,44 @@ nl_parse_flower_eth(struct nlattr **attrs, struct tc_flower *flower)
static void
nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower)
{
+ ovs_be16 encap_ethtype;
+
if (!eth_type_vlan(flower->key.eth_type)) {
return;
}
- flower->key.encap_eth_type =
+ flower->key.encap_eth_type[0] =
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]);
if (attrs[TCA_FLOWER_KEY_VLAN_ID]) {
- flower->key.vlan_id =
+ flower->key.vlan_id[0] =
nl_attr_get_u16(attrs[TCA_FLOWER_KEY_VLAN_ID]);
}
if (attrs[TCA_FLOWER_KEY_VLAN_PRIO]) {
- flower->key.vlan_prio =
+ flower->key.vlan_prio[0] =
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_VLAN_PRIO]);
}
+
+ if (!attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) {
+ return;
+ }
+
+ encap_ethtype = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]);
+ if (!eth_type_vlan(encap_ethtype)) {
+ return;
+ }
+
+ flower->key.encap_eth_type[1] = flower->key.encap_eth_type[0];
+ flower->key.encap_eth_type[0] = encap_ethtype;
+
+ if (attrs[TCA_FLOWER_KEY_CVLAN_ID]) {
+ flower->key.vlan_id[1] =
+ nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CVLAN_ID]);
+ }
+ if (attrs[TCA_FLOWER_KEY_CVLAN_PRIO]) {
+ flower->key.vlan_prio[1] =
+ nl_attr_get_u8(attrs[TCA_FLOWER_KEY_CVLAN_PRIO]);
+ }
}
static void
@@ -1577,6 +1603,7 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
uint16_t host_eth_type = ntohs(flower->key.eth_type);
bool is_vlan = eth_type_vlan(flower->key.eth_type);
+ bool is_qinq = is_vlan && eth_type_vlan(flower->key.encap_eth_type[0]);
int err;
/* need to parse acts first as some acts require changing the matching
@@ -1587,7 +1614,11 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
}
if (is_vlan) {
- host_eth_type = ntohs(flower->key.encap_eth_type);
+ if (is_qinq) {
+ host_eth_type = ntohs(flower->key.encap_eth_type[1]);
+ } else {
+ host_eth_type = ntohs(flower->key.encap_eth_type[0]);
+ }
}
FLOWER_PUT_MASKED_VALUE(dst_mac, TCA_FLOWER_KEY_ETH_DST);
@@ -1631,15 +1662,28 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
nl_msg_put_be16(request, TCA_FLOWER_KEY_ETH_TYPE, flower->key.eth_type);
if (is_vlan) {
- if (flower->key.vlan_id || flower->key.vlan_prio) {
+ if (flower->key.vlan_id[0] || flower->key.vlan_prio[0]) {
nl_msg_put_u16(request, TCA_FLOWER_KEY_VLAN_ID,
- flower->key.vlan_id);
+ flower->key.vlan_id[0]);
nl_msg_put_u8(request, TCA_FLOWER_KEY_VLAN_PRIO,
- flower->key.vlan_prio);
+ flower->key.vlan_prio[0]);
}
- if (flower->key.encap_eth_type) {
+ if (flower->key.encap_eth_type[0]) {
nl_msg_put_be16(request, TCA_FLOWER_KEY_VLAN_ETH_TYPE,
- flower->key.encap_eth_type);
+ flower->key.encap_eth_type[0]);
+ }
+
+ if (is_qinq) {
+ if (flower->key.vlan_id[1] || flower->key.vlan_prio[1]) {
+ nl_msg_put_u16(request, TCA_FLOWER_KEY_CVLAN_ID,
+ flower->key.vlan_id[1]);
+ nl_msg_put_u8(request, TCA_FLOWER_KEY_CVLAN_PRIO,
+ flower->key.vlan_prio[1]);
+ }
+ if (flower->key.encap_eth_type[1]) {
+ nl_msg_put_be16(request, TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
+ flower->key.encap_eth_type[1]);
+ }
}
}
diff --git a/lib/tc.h b/lib/tc.h
index d954819..447d85f 100644
--- a/lib/tc.h
+++ b/lib/tc.h
@@ -26,6 +26,7 @@
#include "netlink-socket.h"
#include "odp-netlink.h"
#include "openvswitch/ofpbuf.h"
+#include "openvswitch/flow.h"
/* For backwards compatability with older kernels */
#ifndef TC_H_CLSACT
@@ -87,10 +88,10 @@ struct tc_flower_key {
ovs_be16 sctp_src;
ovs_be16 sctp_dst;
- uint16_t vlan_id;
- uint8_t vlan_prio;
+ uint16_t vlan_id[FLOW_MAX_VLAN_HEADERS];
+ uint8_t vlan_prio[FLOW_MAX_VLAN_HEADERS];
- ovs_be16 encap_eth_type;
+ ovs_be16 encap_eth_type[FLOW_MAX_VLAN_HEADERS];
uint8_t flags;
uint8_t ip_ttl;
--
2.9.5
More information about the dev
mailing list