Skip to content

Commit

Permalink
Merge branch 'nfp-flower-add-support-for-QinQ-matching'
Browse files Browse the repository at this point in the history
Simon Horman says:

====================
nfp: flower: add support for QinQ matching

Louis says:

Add new feature to the Netronome flower driver to enable QinQ offload.
This needed a bit of gymnastics in order to not break compatibility with
older firmware as the flow key sent to the firmware had to be updated
in order to make space for the extra field.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 20, 2020
2 parents 1e76a2f + 0d630f5 commit 4f6c09f
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 16 deletions.
17 changes: 17 additions & 0 deletions drivers/net/ethernet/netronome/nfp/flower/cmsg.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#define NFP_FLOWER_LAYER_VXLAN BIT(7)

#define NFP_FLOWER_LAYER2_GRE BIT(0)
#define NFP_FLOWER_LAYER2_QINQ BIT(4)
#define NFP_FLOWER_LAYER2_GENEVE BIT(5)
#define NFP_FLOWER_LAYER2_GENEVE_OP BIT(6)
#define NFP_FLOWER_LAYER2_TUN_IPV6 BIT(7)
Expand Down Expand Up @@ -319,6 +320,22 @@ struct nfp_flower_mac_mpls {
__be32 mpls_lse;
};

/* VLAN details (2W/8B)
* 3 2 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | outer_tpid | outer_tci |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | inner_tpid | inner_tci |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct nfp_flower_vlan {
__be16 outer_tpid;
__be16 outer_tci;
__be16 inner_tpid;
__be16 inner_tci;
};

/* L4 ports (for UDP, TCP, SCTP) (1W/4B)
* 3 2 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
Expand Down
6 changes: 5 additions & 1 deletion drivers/net/ethernet/netronome/nfp/flower/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ struct nfp_app;
#define NFP_FLOWER_MASK_ELEMENT_RS 1
#define NFP_FLOWER_MASK_HASH_BITS 10

#define NFP_FLOWER_KEY_MAX_LW 32

#define NFP_FL_META_FLAG_MANAGE_MASK BIT(7)

#define NFP_FL_MASK_REUSE_TIME_NS 40000
Expand All @@ -44,6 +46,7 @@ struct nfp_app;
#define NFP_FL_FEATS_FLOW_MOD BIT(5)
#define NFP_FL_FEATS_PRE_TUN_RULES BIT(6)
#define NFP_FL_FEATS_IPV6_TUN BIT(7)
#define NFP_FL_FEATS_VLAN_QINQ BIT(8)
#define NFP_FL_FEATS_HOST_ACK BIT(31)

#define NFP_FL_ENABLE_FLOW_MERGE BIT(0)
Expand All @@ -57,7 +60,8 @@ struct nfp_app;
NFP_FL_FEATS_VF_RLIM | \
NFP_FL_FEATS_FLOW_MOD | \
NFP_FL_FEATS_PRE_TUN_RULES | \
NFP_FL_FEATS_IPV6_TUN)
NFP_FL_FEATS_IPV6_TUN | \
NFP_FL_FEATS_VLAN_QINQ)

struct nfp_fl_mask_id {
struct circ_buf mask_id_free_list;
Expand Down
73 changes: 70 additions & 3 deletions drivers/net/ethernet/netronome/nfp/flower/match.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
static void
nfp_flower_compile_meta_tci(struct nfp_flower_meta_tci *ext,
struct nfp_flower_meta_tci *msk,
struct flow_rule *rule, u8 key_type)
struct flow_rule *rule, u8 key_type, bool qinq_sup)
{
u16 tmp_tci;

Expand All @@ -24,7 +24,7 @@ nfp_flower_compile_meta_tci(struct nfp_flower_meta_tci *ext,
msk->nfp_flow_key_layer = key_type;
msk->mask_id = ~0;

if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
if (!qinq_sup && flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
struct flow_match_vlan match;

flow_rule_match_vlan(rule, &match);
Expand Down Expand Up @@ -230,6 +230,50 @@ nfp_flower_compile_ip_ext(struct nfp_flower_ip_ext *ext,
}
}

static void
nfp_flower_fill_vlan(struct flow_dissector_key_vlan *key,
struct nfp_flower_vlan *frame,
bool outer_vlan)
{
u16 tci;

tci = NFP_FLOWER_MASK_VLAN_PRESENT;
tci |= FIELD_PREP(NFP_FLOWER_MASK_VLAN_PRIO,
key->vlan_priority) |
FIELD_PREP(NFP_FLOWER_MASK_VLAN_VID,
key->vlan_id);

if (outer_vlan) {
frame->outer_tci = cpu_to_be16(tci);
frame->outer_tpid = key->vlan_tpid;
} else {
frame->inner_tci = cpu_to_be16(tci);
frame->inner_tpid = key->vlan_tpid;
}
}

static void
nfp_flower_compile_vlan(struct nfp_flower_vlan *ext,
struct nfp_flower_vlan *msk,
struct flow_rule *rule)
{
struct flow_match_vlan match;

memset(ext, 0, sizeof(struct nfp_flower_vlan));
memset(msk, 0, sizeof(struct nfp_flower_vlan));

if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
flow_rule_match_vlan(rule, &match);
nfp_flower_fill_vlan(match.key, ext, true);
nfp_flower_fill_vlan(match.mask, msk, true);
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN)) {
flow_rule_match_cvlan(rule, &match);
nfp_flower_fill_vlan(match.key, ext, false);
nfp_flower_fill_vlan(match.mask, msk, false);
}
}

static void
nfp_flower_compile_ipv4(struct nfp_flower_ipv4 *ext,
struct nfp_flower_ipv4 *msk, struct flow_rule *rule)
Expand Down Expand Up @@ -433,7 +477,10 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
struct netlink_ext_ack *extack)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(flow);
struct nfp_flower_priv *priv = app->priv;
bool qinq_sup;
u32 port_id;
int ext_len;
int err;
u8 *ext;
u8 *msk;
Expand All @@ -446,9 +493,11 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
ext = nfp_flow->unmasked_data;
msk = nfp_flow->mask_data;

qinq_sup = !!(priv->flower_ext_feats & NFP_FL_FEATS_VLAN_QINQ);

nfp_flower_compile_meta_tci((struct nfp_flower_meta_tci *)ext,
(struct nfp_flower_meta_tci *)msk,
rule, key_ls->key_layer);
rule, key_ls->key_layer, qinq_sup);
ext += sizeof(struct nfp_flower_meta_tci);
msk += sizeof(struct nfp_flower_meta_tci);

Expand Down Expand Up @@ -547,6 +596,14 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
}
}

if (NFP_FLOWER_LAYER2_QINQ & key_ls->key_layer_two) {
nfp_flower_compile_vlan((struct nfp_flower_vlan *)ext,
(struct nfp_flower_vlan *)msk,
rule);
ext += sizeof(struct nfp_flower_vlan);
msk += sizeof(struct nfp_flower_vlan);
}

if (key_ls->key_layer & NFP_FLOWER_LAYER_VXLAN ||
key_ls->key_layer_two & NFP_FLOWER_LAYER2_GENEVE) {
if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) {
Expand Down Expand Up @@ -589,5 +646,15 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
}
}

/* Check that the flow key does not exceed the maximum limit.
* All structures in the key is multiples of 4 bytes, so use u32.
*/
ext_len = (u32 *)ext - (u32 *)nfp_flow->unmasked_data;
if (ext_len > NFP_FLOWER_KEY_MAX_LW) {
NL_SET_ERR_MSG_MOD(extack,
"unsupported offload: flow key too long");
return -EOPNOTSUPP;
}

return 0;
}
85 changes: 73 additions & 12 deletions drivers/net/ethernet/netronome/nfp/flower/offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
BIT(FLOW_DISSECTOR_KEY_PORTS) | \
BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) | \
BIT(FLOW_DISSECTOR_KEY_VLAN) | \
BIT(FLOW_DISSECTOR_KEY_CVLAN) | \
BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) | \
BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) | \
BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) | \
Expand Down Expand Up @@ -66,7 +67,8 @@
NFP_FLOWER_LAYER_IPV6)

#define NFP_FLOWER_PRE_TUN_RULE_FIELDS \
(NFP_FLOWER_LAYER_PORT | \
(NFP_FLOWER_LAYER_EXT_META | \
NFP_FLOWER_LAYER_PORT | \
NFP_FLOWER_LAYER_MAC | \
NFP_FLOWER_LAYER_IPV4 | \
NFP_FLOWER_LAYER_IPV6)
Expand Down Expand Up @@ -285,6 +287,30 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
NL_SET_ERR_MSG_MOD(extack, "unsupported offload: loaded firmware does not support VLAN PCP offload");
return -EOPNOTSUPP;
}
if (priv->flower_ext_feats & NFP_FL_FEATS_VLAN_QINQ &&
!(key_layer_two & NFP_FLOWER_LAYER2_QINQ)) {
key_layer |= NFP_FLOWER_LAYER_EXT_META;
key_size += sizeof(struct nfp_flower_ext_meta);
key_size += sizeof(struct nfp_flower_vlan);
key_layer_two |= NFP_FLOWER_LAYER2_QINQ;
}
}

if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN)) {
struct flow_match_vlan cvlan;

if (!(priv->flower_ext_feats & NFP_FL_FEATS_VLAN_QINQ)) {
NL_SET_ERR_MSG_MOD(extack, "unsupported offload: loaded firmware does not support VLAN QinQ offload");
return -EOPNOTSUPP;
}

flow_rule_match_vlan(rule, &cvlan);
if (!(key_layer_two & NFP_FLOWER_LAYER2_QINQ)) {
key_layer |= NFP_FLOWER_LAYER_EXT_META;
key_size += sizeof(struct nfp_flower_ext_meta);
key_size += sizeof(struct nfp_flower_vlan);
key_layer_two |= NFP_FLOWER_LAYER2_QINQ;
}
}

if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL)) {
Expand Down Expand Up @@ -1066,6 +1092,7 @@ int nfp_flower_merge_offloaded_flows(struct nfp_app *app,
* nfp_flower_validate_pre_tun_rule()
* @app: Pointer to the APP handle
* @flow: Pointer to NFP flow representation of rule
* @key_ls: Pointer to NFP key layers structure
* @extack: Netlink extended ACK report
*
* Verifies the flow as a pre-tunnel rule.
Expand All @@ -1075,31 +1102,39 @@ int nfp_flower_merge_offloaded_flows(struct nfp_app *app,
static int
nfp_flower_validate_pre_tun_rule(struct nfp_app *app,
struct nfp_fl_payload *flow,
struct nfp_fl_key_ls *key_ls,
struct netlink_ext_ack *extack)
{
struct nfp_flower_priv *priv = app->priv;
struct nfp_flower_meta_tci *meta_tci;
struct nfp_flower_mac_mpls *mac;
u8 *ext = flow->unmasked_data;
struct nfp_fl_act_head *act;
u8 *mask = flow->mask_data;
bool vlan = false;
int act_offset;
u8 key_layer;

meta_tci = (struct nfp_flower_meta_tci *)flow->unmasked_data;
if (meta_tci->tci & cpu_to_be16(NFP_FLOWER_MASK_VLAN_PRESENT)) {
u16 vlan_tci = be16_to_cpu(meta_tci->tci);

vlan_tci &= ~NFP_FLOWER_MASK_VLAN_PRESENT;
flow->pre_tun_rule.vlan_tci = cpu_to_be16(vlan_tci);
vlan = true;
} else {
flow->pre_tun_rule.vlan_tci = cpu_to_be16(0xffff);
key_layer = key_ls->key_layer;
if (!(priv->flower_ext_feats & NFP_FL_FEATS_VLAN_QINQ)) {
if (meta_tci->tci & cpu_to_be16(NFP_FLOWER_MASK_VLAN_PRESENT)) {
u16 vlan_tci = be16_to_cpu(meta_tci->tci);

vlan_tci &= ~NFP_FLOWER_MASK_VLAN_PRESENT;
flow->pre_tun_rule.vlan_tci = cpu_to_be16(vlan_tci);
vlan = true;
} else {
flow->pre_tun_rule.vlan_tci = cpu_to_be16(0xffff);
}
}

key_layer = meta_tci->nfp_flow_key_layer;
if (key_layer & ~NFP_FLOWER_PRE_TUN_RULE_FIELDS) {
NL_SET_ERR_MSG_MOD(extack, "unsupported pre-tunnel rule: too many match fields");
return -EOPNOTSUPP;
} else if (key_ls->key_layer_two & ~NFP_FLOWER_LAYER2_QINQ) {
NL_SET_ERR_MSG_MOD(extack, "unsupported pre-tunnel rule: non-vlan in extended match fields");
return -EOPNOTSUPP;
}

if (!(key_layer & NFP_FLOWER_LAYER_MAC)) {
Expand All @@ -1109,7 +1144,13 @@ nfp_flower_validate_pre_tun_rule(struct nfp_app *app,

/* Skip fields known to exist. */
mask += sizeof(struct nfp_flower_meta_tci);
ext += sizeof(struct nfp_flower_meta_tci);
if (key_ls->key_layer_two) {
mask += sizeof(struct nfp_flower_ext_meta);
ext += sizeof(struct nfp_flower_ext_meta);
}
mask += sizeof(struct nfp_flower_in_port);
ext += sizeof(struct nfp_flower_in_port);

/* Ensure destination MAC address is fully matched. */
mac = (struct nfp_flower_mac_mpls *)mask;
Expand All @@ -1118,6 +1159,8 @@ nfp_flower_validate_pre_tun_rule(struct nfp_app *app,
return -EOPNOTSUPP;
}

mask += sizeof(struct nfp_flower_mac_mpls);
ext += sizeof(struct nfp_flower_mac_mpls);
if (key_layer & NFP_FLOWER_LAYER_IPV4 ||
key_layer & NFP_FLOWER_LAYER_IPV6) {
/* Flags and proto fields have same offset in IPv4 and IPv6. */
Expand All @@ -1130,14 +1173,32 @@ nfp_flower_validate_pre_tun_rule(struct nfp_app *app,
sizeof(struct nfp_flower_ipv4) :
sizeof(struct nfp_flower_ipv6);

mask += sizeof(struct nfp_flower_mac_mpls);

/* Ensure proto and flags are the only IP layer fields. */
for (i = 0; i < size; i++)
if (mask[i] && i != ip_flags && i != ip_proto) {
NL_SET_ERR_MSG_MOD(extack, "unsupported pre-tunnel rule: only flags and proto can be matched in ip header");
return -EOPNOTSUPP;
}
ext += size;
mask += size;
}

if ((priv->flower_ext_feats & NFP_FL_FEATS_VLAN_QINQ)) {
if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_QINQ) {
struct nfp_flower_vlan *vlan_tags;
u16 vlan_tci;

vlan_tags = (struct nfp_flower_vlan *)ext;

vlan_tci = be16_to_cpu(vlan_tags->outer_tci);

vlan_tci &= ~NFP_FLOWER_MASK_VLAN_PRESENT;
flow->pre_tun_rule.vlan_tci = cpu_to_be16(vlan_tci);
vlan = true;
} else {
flow->pre_tun_rule.vlan_tci = cpu_to_be16(0xffff);
}
}

/* Action must be a single egress or pop_vlan and egress. */
Expand Down Expand Up @@ -1220,7 +1281,7 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,
goto err_destroy_flow;

if (flow_pay->pre_tun_rule.dev) {
err = nfp_flower_validate_pre_tun_rule(app, flow_pay, extack);
err = nfp_flower_validate_pre_tun_rule(app, flow_pay, key_layer, extack);
if (err)
goto err_destroy_flow;
}
Expand Down

0 comments on commit 4f6c09f

Please sign in to comment.