@@ -1598,6 +1598,7 @@ dpif_netlink_netdev_match_to_dpif_flow(struct match *match,
.mask = &match->wc.masks,
.support = {
.max_vlan_headers = 2,
+ .recirc = true,
},
};
size_t offset;
@@ -206,9 +206,12 @@ static void
add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid,
struct tc_id *id)
{
- size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
- size_t tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex);
struct ufid_tc_data *new_data = xzalloc(sizeof *new_data);
+ size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
+ size_t tc_hash;
+
+ tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex);
+ tc_hash = hash_int(id->chain, tc_hash);
new_data->ufid = *ufid;
new_data->id = *id;
@@ -252,12 +255,16 @@ get_ufid_tc_mapping(const ovs_u128 *ufid, struct tc_id *id)
static bool
find_ufid(struct netdev *netdev, struct tc_id *id, ovs_u128 *ufid)
{
- size_t tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex);
struct ufid_tc_data *data;
+ size_t tc_hash;
+
+ tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex);
+ tc_hash = hash_int(id->chain, tc_hash);
ovs_mutex_lock(&ufid_lock);
HMAP_FOR_EACH_WITH_HASH(data, tc_to_ufid_node, tc_hash, &tc_to_ufid) {
if (netdev == data->netdev
+ && data->id.chain == id->chain
&& data->id.prio == id->prio
&& data->id.handle == id->handle
&& data->id.hook == id->hook
@@ -706,6 +713,10 @@ parse_tc_flower_to_match(struct tc_flower *flower,
nl_msg_put_u32(buf, OVS_ACTION_ATTR_OUTPUT, odp_to_u32(outport));
}
break;
+ case TC_ACT_GOTO: {
+ nl_msg_put_u32(buf, OVS_ACTION_ATTR_RECIRC, action->chain);
+ }
+ break;
}
}
}
@@ -765,6 +776,7 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump,
match->wc.masks.in_port.odp_port = u32_to_odp(UINT32_MAX);
match->flow.in_port.odp_port = dump->port;
+ match_set_recirc_id(match, id.chain);
return true;
}
@@ -929,12 +941,6 @@ test_key_and_mask(struct match *match)
return EOPNOTSUPP;
}
- if (mask->recirc_id && key->recirc_id) {
- VLOG_DBG_RL(&rl, "offloading attribute recirc_id isn't supported");
- return EOPNOTSUPP;
- }
- mask->recirc_id = 0;
-
if (mask->dp_hash) {
VLOG_DBG_RL(&rl, "offloading attribute dp_hash isn't supported");
return EOPNOTSUPP;
@@ -1102,6 +1108,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
uint32_t block_id = 0;
struct nlattr *nla;
struct tc_id id;
+ uint32_t chain;
size_t left;
int prio = 0;
int ifindex;
@@ -1116,6 +1123,9 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
memset(&flower, 0, sizeof flower);
+ chain = key->recirc_id;
+ mask->recirc_id = 0;
+
if (flow_tnl_dst_is_set(&key->tunnel)) {
VLOG_DBG_RL(&rl,
"tunnel: id %#" PRIx64 " src " IP_FMT
@@ -1348,6 +1358,10 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
if (err) {
return err;
}
+ } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_RECIRC) {
+ action->type = TC_ACT_GOTO;
+ action->chain = nl_attr_get_u32(nla);
+ flower.action_count++;
} else {
VLOG_DBG_RL(&rl, "unsupported put action type: %d",
nl_attr_type(nla));
@@ -1370,7 +1384,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
flower.act_cookie.data = ufid;
flower.act_cookie.len = sizeof *ufid;
- id = make_tc_id(ifindex, block_id, prio, hook);
+ id = make_tc_id_chain(ifindex, block_id, chain, prio, hook);
err = tc_replace_flower(&id, &flower);
if (!err) {
add_ufid_tc_mapping(netdev, ufid, &id);
@@ -1414,6 +1428,7 @@ netdev_tc_flow_get(struct netdev *netdev,
match->wc.masks.in_port.odp_port = u32_to_odp(UINT32_MAX);
match->flow.in_port.odp_port = in_port;
+ match_set_recirc_id(match, id.chain);
return 0;
}
@@ -50,6 +50,7 @@
#endif
#if TCA_MAX < 14
+#define TCA_CHAIN 11
#define TCA_INGRESS_BLOCK 13
#endif
@@ -205,6 +206,10 @@ static void request_from_tc_id(struct tc_id *id, uint16_t eth_type,
TC_EGRESS_PARENT : (id->block_id ? : TC_INGRESS_PARENT);
tcmsg->tcm_info = tc_make_handle(id->prio, eth_type);
tcmsg->tcm_handle = id->handle;
+
+ if (id->chain) {
+ nl_msg_put_u32(request, TCA_CHAIN, id->chain);
+ }
}
@@ -285,6 +290,7 @@ tc_add_del_qdisc(int ifindex, bool add, uint32_t block_id,
static const struct nl_policy tca_policy[] = {
[TCA_KIND] = { .type = NL_A_STRING, .optional = false, },
[TCA_OPTIONS] = { .type = NL_A_NESTED, .optional = false, },
+ [TCA_CHAIN] = { .type = NL_A_U32, .optional = true, },
[TCA_STATS] = { .type = NL_A_UNSPEC,
.min_len = sizeof(struct tc_stats), .optional = true, },
[TCA_STATS2] = { .type = NL_A_NESTED, .optional = true, },
@@ -1128,12 +1134,13 @@ nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
}
static int
-nl_parse_act_drop(struct nlattr *options, struct tc_flower *flower)
+nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
{
struct nlattr *gact_attrs[ARRAY_SIZE(gact_policy)];
const struct tc_gact *p;
struct nlattr *gact_parms;
const struct tcf_t *tm;
+ struct tc_action *action;
if (!nl_parse_nested(options, gact_policy, gact_attrs,
ARRAY_SIZE(gact_policy))) {
@@ -1144,7 +1151,11 @@ nl_parse_act_drop(struct nlattr *options, struct tc_flower *flower)
gact_parms = gact_attrs[TCA_GACT_PARMS];
p = nl_attr_get_unspec(gact_parms, sizeof *p);
- if (p->action != TC_ACT_SHOT) {
+ if (TC_ACT_EXT_CMP(p->action, TC_ACT_GOTO_CHAIN)) {
+ action = &flower->actions[flower->action_count++];
+ action->chain = p->action & TC_ACT_EXT_VAL_MASK;
+ action->type = TC_ACT_GOTO;
+ } else if (p->action != TC_ACT_SHOT) {
VLOG_ERR_RL(&error_rl, "unknown gact action: %d", p->action);
return EINVAL;
}
@@ -1329,7 +1340,7 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower)
act_cookie = action_attrs[TCA_ACT_COOKIE];
if (!strcmp(act_kind, "gact")) {
- err = nl_parse_act_drop(act_options, flower);
+ err = nl_parse_act_gact(act_options, flower);
} else if (!strcmp(act_kind, "mirred")) {
err = nl_parse_act_mirred(act_options, flower);
} else if (!strcmp(act_kind, "vlan")) {
@@ -1478,6 +1489,10 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_id *id,
return EPROTO;
}
+ if (ta[TCA_CHAIN]) {
+ id->chain = nl_attr_get_u32(ta[TCA_CHAIN]);
+ }
+
kind = nl_attr_get_string(ta[TCA_KIND]);
if (strcmp(kind, "flower")) {
VLOG_DBG_ONCE("Unsupported filter: %s", kind);
@@ -1714,7 +1729,7 @@ nl_msg_put_act_tunnel_key_set(struct ofpbuf *request, bool id_present,
}
static void
-nl_msg_put_act_drop(struct ofpbuf *request)
+nl_msg_put_act_gact(struct ofpbuf *request, uint32_t chain)
{
size_t offset;
@@ -1723,6 +1738,10 @@ nl_msg_put_act_drop(struct ofpbuf *request)
{
struct tc_gact p = { .action = TC_ACT_SHOT };
+ if (chain) {
+ p.action = TC_ACT_GOTO_CHAIN | chain;
+ }
+
nl_msg_put_unspec(request, TCA_GACT_PARMS, &p, sizeof p);
}
nl_msg_end_nested(request, offset);
@@ -2032,12 +2051,20 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
nl_msg_end_nested(request, act_offset);
}
break;
+ case TC_ACT_GOTO: {
+ act_offset = nl_msg_start_nested(request, act_index++);
+ nl_msg_put_act_gact(request, action->chain);
+ nl_msg_put_act_cookie(request, &flower->act_cookie);
+ nl_msg_end_nested(request, act_offset);
+ }
+ break;
}
}
}
- if (!ifindex) {
+
+ if (!flower->action_count) {
act_offset = nl_msg_start_nested(request, act_index++);
- nl_msg_put_act_drop(request);
+ nl_msg_put_act_gact(request, 0);
nl_msg_put_act_cookie(request, &flower->act_cookie);
nl_msg_end_nested(request, act_offset);
}
@@ -2280,6 +2307,16 @@ struct tc_id make_tc_id(int ifindex, uint32_t block_id, uint16_t prio,
return id;
}
+struct tc_id make_tc_id_chain(int ifindex, uint32_t block_id, uint32_t chain,
+ uint16_t prio, enum tc_qdisc_hook hook)
+{
+ struct tc_id id = make_tc_id(ifindex, block_id, prio, hook);
+
+ id.chain = chain;
+
+ return id;
+}
+
int
tc_replace_flower(struct tc_id *id, struct tc_flower *flower)
{
@@ -153,10 +153,13 @@ enum tc_action_type {
TC_ACT_PEDIT,
TC_ACT_VLAN_POP,
TC_ACT_VLAN_PUSH,
+ TC_ACT_GOTO,
};
struct tc_action {
union {
+ int chain;
+
struct {
int ifindex_out;
bool ingress;
@@ -201,12 +204,15 @@ struct tc_id {
enum tc_qdisc_hook hook;
uint32_t block_id;
int ifindex;
+ uint32_t chain;
uint16_t prio;
uint32_t handle;
};
struct tc_id make_tc_id(int ifindex, uint32_t block_id, uint16_t prio,
enum tc_qdisc_hook hook);
+struct tc_id make_tc_id_chain(int ifindex, uint32_t block_id, uint32_t chain,
+ uint16_t prio, enum tc_qdisc_hook hook);
struct tc_flower {
struct tc_flower_key key;
Each recirculation id will create a tc chain, and we translate the recirculation action to a tc goto chain action. Signed-off-by: Paul Blakey <paulb@mellanox.com> --- lib/dpif-netlink.c | 1 + lib/netdev-offload-tc.c | 35 +++++++++++++++++++++++++---------- lib/tc.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ lib/tc.h | 6 ++++++ 4 files changed, 75 insertions(+), 16 deletions(-)