mirror of
https://github.com/openvswitch/ovs
synced 2025-08-30 22:05:19 +00:00
tc: allow offloading of block ids
Blocks, in tc classifiers, allow the grouping of multiple qdiscs with an associated block id. Whenever a filter is added to/removed from this block, the filter is added to/removed from all associated qdiscs. Extend TC offload functions to take a block id as a parameter. If the id is zero then the dqisc is not considered part of a block. Signed-off-by: John Hurley <john.hurley@netronome.com> Reviewed-by: Simon Horman <simon.horman@netronome.com> Reviewed-by: Dirk van der Merwe <dirk.vandermerwe@netronome.com> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Simon Horman <simon.horman@netronome.com>
This commit is contained in:
committed by
Simon Horman
parent
e461481330
commit
093c9458fb
@@ -2275,7 +2275,7 @@ netdev_linux_set_policing(struct netdev *netdev_,
|
||||
|
||||
COVERAGE_INC(netdev_set_policing);
|
||||
/* Remove any existing ingress qdisc. */
|
||||
error = tc_add_del_ingress_qdisc(ifindex, false);
|
||||
error = tc_add_del_ingress_qdisc(ifindex, false, 0);
|
||||
if (error) {
|
||||
VLOG_WARN_RL(&rl, "%s: removing policing failed: %s",
|
||||
netdev_name, ovs_strerror(error));
|
||||
@@ -2283,7 +2283,7 @@ netdev_linux_set_policing(struct netdev *netdev_,
|
||||
}
|
||||
|
||||
if (kbits_rate) {
|
||||
error = tc_add_del_ingress_qdisc(ifindex, true);
|
||||
error = tc_add_del_ingress_qdisc(ifindex, true, 0);
|
||||
if (error) {
|
||||
VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s",
|
||||
netdev_name, ovs_strerror(error));
|
||||
|
@@ -44,6 +44,7 @@ static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
|
||||
|
||||
static struct hmap ufid_tc = HMAP_INITIALIZER(&ufid_tc);
|
||||
static bool multi_mask_per_prio = false;
|
||||
static bool block_support = false;
|
||||
|
||||
struct netlink_field {
|
||||
int offset;
|
||||
@@ -307,6 +308,7 @@ int
|
||||
netdev_tc_flow_flush(struct netdev *netdev)
|
||||
{
|
||||
int ifindex = netdev_get_ifindex(netdev);
|
||||
uint32_t block_id = 0;
|
||||
|
||||
if (ifindex < 0) {
|
||||
VLOG_ERR_RL(&error_rl, "flow_flush: failed to get ifindex for %s: %s",
|
||||
@@ -314,7 +316,7 @@ netdev_tc_flow_flush(struct netdev *netdev)
|
||||
return -ifindex;
|
||||
}
|
||||
|
||||
return tc_flush(ifindex);
|
||||
return tc_flush(ifindex, block_id);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -322,6 +324,7 @@ netdev_tc_flow_dump_create(struct netdev *netdev,
|
||||
struct netdev_flow_dump **dump_out)
|
||||
{
|
||||
struct netdev_flow_dump *dump;
|
||||
uint32_t block_id = 0;
|
||||
int ifindex;
|
||||
|
||||
ifindex = netdev_get_ifindex(netdev);
|
||||
@@ -334,7 +337,7 @@ netdev_tc_flow_dump_create(struct netdev *netdev,
|
||||
dump = xzalloc(sizeof *dump);
|
||||
dump->nl_dump = xzalloc(sizeof *dump->nl_dump);
|
||||
dump->netdev = netdev_ref(netdev);
|
||||
tc_dump_flower_start(ifindex, dump->nl_dump);
|
||||
tc_dump_flower_start(ifindex, dump->nl_dump, block_id);
|
||||
|
||||
*dump_out = dump;
|
||||
|
||||
@@ -890,6 +893,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
|
||||
struct flow *mask = &match->wc.masks;
|
||||
const struct flow_tnl *tnl = &match->flow.tunnel;
|
||||
struct tc_action *action;
|
||||
uint32_t block_id = 0;
|
||||
struct nlattr *nla;
|
||||
size_t left;
|
||||
int prio = 0;
|
||||
@@ -1097,7 +1101,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
|
||||
handle = get_ufid_tc_mapping(ufid, &prio, NULL);
|
||||
if (handle && prio) {
|
||||
VLOG_DBG_RL(&rl, "updating old handle: %d prio: %d", handle, prio);
|
||||
tc_del_filter(ifindex, prio, handle);
|
||||
tc_del_filter(ifindex, prio, handle, block_id);
|
||||
}
|
||||
|
||||
if (!prio) {
|
||||
@@ -1111,7 +1115,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
|
||||
flower.act_cookie.data = ufid;
|
||||
flower.act_cookie.len = sizeof *ufid;
|
||||
|
||||
err = tc_replace_flower(ifindex, prio, handle, &flower);
|
||||
err = tc_replace_flower(ifindex, prio, handle, &flower, block_id);
|
||||
if (!err) {
|
||||
add_ufid_tc_mapping(ufid, flower.prio, flower.handle, netdev, ifindex);
|
||||
}
|
||||
@@ -1131,6 +1135,7 @@ netdev_tc_flow_get(struct netdev *netdev OVS_UNUSED,
|
||||
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
|
||||
struct netdev *dev;
|
||||
struct tc_flower flower;
|
||||
uint32_t block_id = 0;
|
||||
odp_port_t in_port;
|
||||
int prio = 0;
|
||||
int ifindex;
|
||||
@@ -1152,7 +1157,7 @@ netdev_tc_flow_get(struct netdev *netdev OVS_UNUSED,
|
||||
|
||||
VLOG_DBG_RL(&rl, "flow get (dev %s prio %d handle %d)",
|
||||
netdev_get_name(dev), prio, handle);
|
||||
err = tc_get_flower(ifindex, prio, handle, &flower);
|
||||
err = tc_get_flower(ifindex, prio, handle, &flower, block_id);
|
||||
netdev_close(dev);
|
||||
if (err) {
|
||||
VLOG_ERR_RL(&error_rl, "flow get failed (dev %s prio %d handle %d): %s",
|
||||
@@ -1175,6 +1180,7 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
|
||||
struct dpif_flow_stats *stats)
|
||||
{
|
||||
struct tc_flower flower;
|
||||
uint32_t block_id = 0;
|
||||
struct netdev *dev;
|
||||
int prio = 0;
|
||||
int ifindex;
|
||||
@@ -1196,14 +1202,14 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
|
||||
|
||||
if (stats) {
|
||||
memset(stats, 0, sizeof *stats);
|
||||
if (!tc_get_flower(ifindex, prio, handle, &flower)) {
|
||||
if (!tc_get_flower(ifindex, prio, handle, &flower, block_id)) {
|
||||
stats->n_packets = get_32aligned_u64(&flower.stats.n_packets);
|
||||
stats->n_bytes = get_32aligned_u64(&flower.stats.n_bytes);
|
||||
stats->used = flower.lastused;
|
||||
}
|
||||
}
|
||||
|
||||
error = tc_del_filter(ifindex, prio, handle);
|
||||
error = tc_del_filter(ifindex, prio, handle, block_id);
|
||||
del_ufid_tc_mapping(ufid);
|
||||
|
||||
netdev_close(dev);
|
||||
@@ -1212,7 +1218,7 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
|
||||
}
|
||||
|
||||
static void
|
||||
probe_multi_mask_per_prio(int ifindex)
|
||||
probe_multi_mask_per_prio(int ifindex, uint32_t block_id)
|
||||
{
|
||||
struct tc_flower flower;
|
||||
int error;
|
||||
@@ -1224,7 +1230,7 @@ probe_multi_mask_per_prio(int ifindex)
|
||||
memset(&flower.key.dst_mac, 0x11, sizeof flower.key.dst_mac);
|
||||
memset(&flower.mask.dst_mac, 0xff, sizeof flower.mask.dst_mac);
|
||||
|
||||
error = tc_replace_flower(ifindex, 1, 1, &flower);
|
||||
error = tc_replace_flower(ifindex, 1, 1, &flower, block_id);
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
@@ -1232,23 +1238,42 @@ probe_multi_mask_per_prio(int ifindex)
|
||||
memset(&flower.key.src_mac, 0x11, sizeof flower.key.src_mac);
|
||||
memset(&flower.mask.src_mac, 0xff, sizeof flower.mask.src_mac);
|
||||
|
||||
error = tc_replace_flower(ifindex, 1, 2, &flower);
|
||||
tc_del_filter(ifindex, 1, 1);
|
||||
error = tc_replace_flower(ifindex, 1, 2, &flower, block_id);
|
||||
tc_del_filter(ifindex, 1, 1, block_id);
|
||||
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
tc_del_filter(ifindex, 1, 2);
|
||||
tc_del_filter(ifindex, 1, 2, block_id);
|
||||
|
||||
multi_mask_per_prio = true;
|
||||
VLOG_INFO("probe tc: multiple masks on single tc prio is supported.");
|
||||
}
|
||||
|
||||
static void
|
||||
probe_tc_block_support(int ifindex)
|
||||
{
|
||||
uint32_t block_id = 1;
|
||||
int error;
|
||||
|
||||
error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
tc_add_del_ingress_qdisc(ifindex, false, block_id);
|
||||
|
||||
block_support = true;
|
||||
VLOG_INFO("probe tc: block offload is supported.");
|
||||
}
|
||||
|
||||
int
|
||||
netdev_tc_init_flow_api(struct netdev *netdev)
|
||||
{
|
||||
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
|
||||
static struct ovsthread_once multi_mask_once = OVSTHREAD_ONCE_INITIALIZER;
|
||||
static struct ovsthread_once block_once = OVSTHREAD_ONCE_INITIALIZER;
|
||||
uint32_t block_id = 0;
|
||||
int ifindex;
|
||||
int error;
|
||||
|
||||
@@ -1259,7 +1284,12 @@ netdev_tc_init_flow_api(struct netdev *netdev)
|
||||
return -ifindex;
|
||||
}
|
||||
|
||||
error = tc_add_del_ingress_qdisc(ifindex, true);
|
||||
if (ovsthread_once_start(&block_once)) {
|
||||
probe_tc_block_support(ifindex);
|
||||
ovsthread_once_done(&block_once);
|
||||
}
|
||||
|
||||
error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
|
||||
|
||||
if (error && error != EEXIST) {
|
||||
VLOG_ERR("failed adding ingress qdisc required for offloading: %s",
|
||||
@@ -1269,9 +1299,9 @@ netdev_tc_init_flow_api(struct netdev *netdev)
|
||||
|
||||
VLOG_INFO("added ingress qdisc to %s", netdev_get_name(netdev));
|
||||
|
||||
if (ovsthread_once_start(&once)) {
|
||||
probe_multi_mask_per_prio(ifindex);
|
||||
ovsthread_once_done(&once);
|
||||
if (ovsthread_once_start(&multi_mask_once)) {
|
||||
probe_multi_mask_per_prio(ifindex, block_id);
|
||||
ovsthread_once_done(&multi_mask_once);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
60
lib/tc.c
60
lib/tc.c
@@ -43,6 +43,14 @@
|
||||
|
||||
#define MAX_PEDIT_OFFSETS 32
|
||||
|
||||
#ifndef TCM_IFINDEX_MAGIC_BLOCK
|
||||
#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU)
|
||||
#endif
|
||||
|
||||
#if TCA_MAX < 14
|
||||
#define TCA_INGRESS_BLOCK 13
|
||||
#endif
|
||||
|
||||
VLOG_DEFINE_THIS_MODULE(tc);
|
||||
|
||||
static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
|
||||
@@ -173,10 +181,14 @@ tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
|
||||
* The configuration and stats may be seen with the following command:
|
||||
* /sbin/tc -s qdisc show dev <devname>
|
||||
*
|
||||
* If block_id is greater than 0, then the ingress qdisc is added to a block.
|
||||
* In this case, it is equivalent to running (when 'add' is true):
|
||||
* /sbin/tc qdisc add dev <devname> ingress_block <block_id> ingress
|
||||
*
|
||||
* Returns 0 if successful, otherwise a positive errno value.
|
||||
*/
|
||||
int
|
||||
tc_add_del_ingress_qdisc(int ifindex, bool add)
|
||||
tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id)
|
||||
{
|
||||
struct ofpbuf request;
|
||||
struct tcmsg *tcmsg;
|
||||
@@ -189,6 +201,9 @@ tc_add_del_ingress_qdisc(int ifindex, bool add)
|
||||
tcmsg->tcm_parent = TC_H_INGRESS;
|
||||
nl_msg_put_string(&request, TCA_KIND, "ingress");
|
||||
nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0);
|
||||
if (block_id) {
|
||||
nl_msg_put_u32(&request, TCA_INGRESS_BLOCK, block_id);
|
||||
}
|
||||
|
||||
error = tc_transact(&request, NULL);
|
||||
if (error) {
|
||||
@@ -1007,13 +1022,15 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower)
|
||||
}
|
||||
|
||||
int
|
||||
tc_dump_flower_start(int ifindex, struct nl_dump *dump)
|
||||
tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
|
||||
{
|
||||
struct ofpbuf request;
|
||||
struct tcmsg *tcmsg;
|
||||
int index;
|
||||
|
||||
tcmsg = tc_make_request(ifindex, RTM_GETTFILTER, NLM_F_DUMP, &request);
|
||||
tcmsg->tcm_parent = TC_INGRESS_PARENT;
|
||||
index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
|
||||
tcmsg = tc_make_request(index, RTM_GETTFILTER, NLM_F_DUMP, &request);
|
||||
tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
|
||||
tcmsg->tcm_info = TC_H_UNSPEC;
|
||||
tcmsg->tcm_handle = 0;
|
||||
|
||||
@@ -1024,28 +1041,32 @@ tc_dump_flower_start(int ifindex, struct nl_dump *dump)
|
||||
}
|
||||
|
||||
int
|
||||
tc_flush(int ifindex)
|
||||
tc_flush(int ifindex, uint32_t block_id)
|
||||
{
|
||||
struct ofpbuf request;
|
||||
struct tcmsg *tcmsg;
|
||||
int index;
|
||||
|
||||
tcmsg = tc_make_request(ifindex, RTM_DELTFILTER, NLM_F_ACK, &request);
|
||||
tcmsg->tcm_parent = TC_INGRESS_PARENT;
|
||||
index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
|
||||
tcmsg = tc_make_request(index, RTM_DELTFILTER, NLM_F_ACK, &request);
|
||||
tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
|
||||
tcmsg->tcm_info = TC_H_UNSPEC;
|
||||
|
||||
return tc_transact(&request, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
tc_del_filter(int ifindex, int prio, int handle)
|
||||
tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id)
|
||||
{
|
||||
struct ofpbuf request;
|
||||
struct tcmsg *tcmsg;
|
||||
struct ofpbuf *reply;
|
||||
int error;
|
||||
int index;
|
||||
|
||||
tcmsg = tc_make_request(ifindex, RTM_DELTFILTER, NLM_F_ECHO, &request);
|
||||
tcmsg->tcm_parent = TC_INGRESS_PARENT;
|
||||
index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
|
||||
tcmsg = tc_make_request(index, RTM_DELTFILTER, NLM_F_ECHO, &request);
|
||||
tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
|
||||
tcmsg->tcm_info = tc_make_handle(prio, 0);
|
||||
tcmsg->tcm_handle = handle;
|
||||
|
||||
@@ -1057,15 +1078,18 @@ tc_del_filter(int ifindex, int prio, int handle)
|
||||
}
|
||||
|
||||
int
|
||||
tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower)
|
||||
tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower,
|
||||
uint32_t block_id)
|
||||
{
|
||||
struct ofpbuf request;
|
||||
struct tcmsg *tcmsg;
|
||||
struct ofpbuf *reply;
|
||||
int error;
|
||||
int index;
|
||||
|
||||
tcmsg = tc_make_request(ifindex, RTM_GETTFILTER, NLM_F_ECHO, &request);
|
||||
tcmsg->tcm_parent = TC_INGRESS_PARENT;
|
||||
index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
|
||||
tcmsg = tc_make_request(index, RTM_GETTFILTER, NLM_F_ECHO, &request);
|
||||
tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
|
||||
tcmsg->tcm_info = tc_make_handle(prio, 0);
|
||||
tcmsg->tcm_handle = handle;
|
||||
|
||||
@@ -1625,7 +1649,7 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
|
||||
|
||||
int
|
||||
tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
|
||||
struct tc_flower *flower)
|
||||
struct tc_flower *flower, uint32_t block_id)
|
||||
{
|
||||
struct ofpbuf request;
|
||||
struct tcmsg *tcmsg;
|
||||
@@ -1633,10 +1657,12 @@ tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
|
||||
int error = 0;
|
||||
size_t basic_offset;
|
||||
uint16_t eth_type = (OVS_FORCE uint16_t) flower->key.eth_type;
|
||||
int index;
|
||||
|
||||
tcmsg = tc_make_request(ifindex, RTM_NEWTFILTER,
|
||||
NLM_F_CREATE | NLM_F_ECHO, &request);
|
||||
tcmsg->tcm_parent = TC_INGRESS_PARENT;
|
||||
index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
|
||||
tcmsg = tc_make_request(index, RTM_NEWTFILTER, NLM_F_CREATE | NLM_F_ECHO,
|
||||
&request);
|
||||
tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
|
||||
tcmsg->tcm_info = tc_make_handle(prio, eth_type);
|
||||
tcmsg->tcm_handle = handle;
|
||||
|
||||
|
12
lib/tc.h
12
lib/tc.h
@@ -63,7 +63,7 @@ tc_get_minor(unsigned int handle)
|
||||
struct tcmsg *tc_make_request(int ifindex, int type,
|
||||
unsigned int flags, struct ofpbuf *);
|
||||
int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp);
|
||||
int tc_add_del_ingress_qdisc(int ifindex, bool add);
|
||||
int tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id);
|
||||
|
||||
struct tc_cookie {
|
||||
const void *data;
|
||||
@@ -198,12 +198,12 @@ BUILD_ASSERT_DECL(offsetof(struct tc_flower, rewrite)
|
||||
+ sizeof(uint32_t) - 2 < sizeof(struct tc_flower));
|
||||
|
||||
int tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
|
||||
struct tc_flower *flower);
|
||||
int tc_del_filter(int ifindex, int prio, int handle);
|
||||
struct tc_flower *flower, uint32_t block_id);
|
||||
int tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id);
|
||||
int tc_get_flower(int ifindex, int prio, int handle,
|
||||
struct tc_flower *flower);
|
||||
int tc_flush(int ifindex);
|
||||
int tc_dump_flower_start(int ifindex, struct nl_dump *dump);
|
||||
struct tc_flower *flower, uint32_t block_id);
|
||||
int tc_flush(int ifindex, uint32_t block_id);
|
||||
int tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id);
|
||||
int parse_netlink_to_tc_flower(struct ofpbuf *reply,
|
||||
struct tc_flower *flower);
|
||||
void tc_set_policy(const char *policy);
|
||||
|
Reference in New Issue
Block a user