Skip to content

Commit

Permalink
Add support to offload QinQ double VLAN headers match
Browse files Browse the repository at this point in the history
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@mellanox.com>
Reviewed-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
  • Loading branch information
jialiu02 authored and shorman-netronome committed Jul 25, 2018
1 parent 2f9366b commit f9885dc
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 27 deletions.
6 changes: 3 additions & 3 deletions acinclude.m4
Expand Up @@ -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>], [
Expand Down
6 changes: 5 additions & 1 deletion 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

Expand Down Expand Up @@ -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,
};

Expand Down
2 changes: 1 addition & 1 deletion lib/dpif-netlink.c
Expand Up @@ -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;
Expand Down
58 changes: 48 additions & 10 deletions lib/netdev-tc-offloads.c
Expand Up @@ -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), 0);
match_set_dl_vlan_pcp(match, key->vlan_prio, 0);
match_set_dl_type(match, key->encap_eth_type);
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);
Expand Down Expand Up @@ -954,14 +965,15 @@ 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) {
Expand All @@ -970,8 +982,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);

Expand Down
62 changes: 53 additions & 9 deletions lib/tc.c
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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]);
}
}
}

Expand Down
7 changes: 4 additions & 3 deletions lib/tc.h
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit f9885dc

Please sign in to comment.