[ovs-dev,RFC,OVS,1/2] ovs-tc: allow offloading TC rules to egress qdiscs
diff mbox series

Message ID 1551292128-4632-2-git-send-email-john.hurley@netronome.com
State Superseded
Delegated to: Simon Horman
Headers show
Series
  • ovs-tc: support OvS internal port offload
Related show

Commit Message

John Hurley Feb. 27, 2019, 6:28 p.m. UTC
Offloading rules to a TC datapath only allows the creating of ingress hook
qdiscs and the application of filters to these. However, there may be
certain situations where an egress qdisc is more applicable (e.g. when
offloading to TC rules applied to OvS internal ports).

Extend the TC API in OvS to allow the creation of egress qdisc and to add
or interact with flower filters applied to these.

Signed-off-by: John Hurley <john.hurley@netronome.com>
Reviewed-by: Simon Horman <simon.horman@netronome.com>
---
 lib/netdev-linux.c       |  8 ++++----
 lib/netdev-tc-offloads.c | 30 +++++++++++++++---------------
 lib/tc.c                 | 45 ++++++++++++++++++++++++++++++---------------
 lib/tc.h                 | 20 ++++++++++++++------
 4 files changed, 63 insertions(+), 40 deletions(-)

Comments

Roi Dayan March 5, 2019, 2:12 p.m. UTC | #1
On 27/02/2019 20:28, John Hurley wrote:
> Offloading rules to a TC datapath only allows the creating of ingress hook
> qdiscs and the application of filters to these. However, there may be
> certain situations where an egress qdisc is more applicable (e.g. when
> offloading to TC rules applied to OvS internal ports).
> 
> Extend the TC API in OvS to allow the creation of egress qdisc and to add
> or interact with flower filters applied to these.
> 
> Signed-off-by: John Hurley <john.hurley@netronome.com>
> Reviewed-by: Simon Horman <simon.horman@netronome.com>
> ---
>  lib/netdev-linux.c       |  8 ++++----
>  lib/netdev-tc-offloads.c | 30 +++++++++++++++---------------
>  lib/tc.c                 | 45 ++++++++++++++++++++++++++++++---------------
>  lib/tc.h                 | 20 ++++++++++++++------
>  4 files changed, 63 insertions(+), 40 deletions(-)
> 
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index 25d037c..8d37eb6 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -740,7 +740,7 @@ netdev_linux_update_lag(struct rtnetlink_change *change)
>  
>                  /* LAG master is linux netdev so add slave to same block. */
>                  error = tc_add_del_ingress_qdisc(change->if_index, true,
> -                                                 block_id);
> +                                                 block_id, false);
>                  if (error) {
>                      VLOG_WARN("failed to bind LAG slave to master's block");
>                      shash_delete(&lag_shash, lag->node);
> @@ -756,7 +756,7 @@ netdev_linux_update_lag(struct rtnetlink_change *change)
>  
>          if (lag) {
>              tc_add_del_ingress_qdisc(change->if_index, false,
> -                                     lag->block_id);
> +                                     lag->block_id, false);
>              shash_delete(&lag_shash, lag->node);
>              free(lag);
>          }
> @@ -2370,7 +2370,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, 0);
> +    error = tc_add_del_ingress_qdisc(ifindex, false, 0, false);
>      if (error) {
>          VLOG_WARN_RL(&rl, "%s: removing policing failed: %s",
>                       netdev_name, ovs_strerror(error));
> @@ -2378,7 +2378,7 @@ netdev_linux_set_policing(struct netdev *netdev_,
>      }
>  
>      if (kbits_rate) {
> -        error = tc_add_del_ingress_qdisc(ifindex, true, 0);
> +        error = tc_add_del_ingress_qdisc(ifindex, true, 0, false);
>          if (error) {
>              VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s",
>                           netdev_name, ovs_strerror(error));

you are missing a call in tc_del_matchall_policer 

lib/netdev-linux.c: In function ‘tc_del_matchall_policer’:
lib/netdev-linux.c:2426:11: error: too few arguments to function ‘tc_del_filter’
     err = tc_del_filter(ifindex, TC_RESERVED_PRIORITY_POLICE, 1, block_id);
           ^~~~~~~~~~~~~



> diff --git a/lib/netdev-tc-offloads.c b/lib/netdev-tc-offloads.c
> index 36bce15..31ad9f4 100644
> --- a/lib/netdev-tc-offloads.c
> +++ b/lib/netdev-tc-offloads.c
> @@ -182,7 +182,7 @@ del_filter_and_ufid_mapping(int ifindex, int prio, int handle,
>  {
>      int err;
>  
> -    err = tc_del_filter(ifindex, prio, handle, block_id);
> +    err = tc_del_filter(ifindex, prio, handle, block_id, false);
>      del_ufid_tc_mapping(ufid);
>  
>      return err;
> @@ -350,7 +350,7 @@ netdev_tc_flow_flush(struct netdev *netdev)
>  
>      block_id = get_block_id_from_netdev(netdev);
>  
> -    return tc_flush(ifindex, block_id);
> +    return tc_flush(ifindex, block_id, false);
>  }
>  
>  int
> @@ -372,7 +372,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, block_id);
> +    tc_dump_flower_start(ifindex, dump->nl_dump, block_id, false);
>  
>      *dump_out = dump;
>  
> @@ -1347,7 +1347,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, block_id);
> +    err = tc_replace_flower(ifindex, prio, handle, &flower, block_id, false);
>      if (!err) {
>          add_ufid_tc_mapping(ufid, flower.prio, flower.handle, netdev, ifindex);
>      }
> @@ -1390,7 +1390,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);
>      block_id = get_block_id_from_netdev(netdev);
> -    err = tc_get_flower(ifindex, prio, handle, &flower, block_id);
> +    err = tc_get_flower(ifindex, prio, handle, &flower, block_id, false);
>      netdev_close(dev);
>      if (err) {
>          VLOG_ERR_RL(&error_rl, "flow get failed (dev %s prio %d handle %d): %s",
> @@ -1437,7 +1437,7 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
>  
>      if (stats) {
>          memset(stats, 0, sizeof *stats);
> -        if (!tc_get_flower(ifindex, prio, handle, &flower, block_id)) {
> +        if (!tc_get_flower(ifindex, prio, handle, &flower, block_id, false)) {
>              stats->n_packets = get_32aligned_u64(&flower.stats.n_packets);
>              stats->n_bytes = get_32aligned_u64(&flower.stats.n_bytes);
>              stats->used = flower.lastused;
> @@ -1458,7 +1458,7 @@ probe_multi_mask_per_prio(int ifindex)
>      int block_id = 0;
>      int error;
>  
> -    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
> +    error = tc_add_del_ingress_qdisc(ifindex, true, block_id, false);
>      if (error) {
>          return;
>      }
> @@ -1470,7 +1470,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, block_id);
> +    error = tc_replace_flower(ifindex, 1, 1, &flower, block_id, false);
>      if (error) {
>          goto out;
>      }
> @@ -1478,20 +1478,20 @@ 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, block_id);
> -    tc_del_filter(ifindex, 1, 1, block_id);
> +    error = tc_replace_flower(ifindex, 1, 2, &flower, block_id, false);
> +    tc_del_filter(ifindex, 1, 1, block_id, false);
>  
>      if (error) {
>          goto out;
>      }
>  
> -    tc_del_filter(ifindex, 1, 2, block_id);
> +    tc_del_filter(ifindex, 1, 2, block_id, false);
>  
>      multi_mask_per_prio = true;
>      VLOG_INFO("probe tc: multiple masks on single tc prio is supported.");
>  
>  out:
> -    tc_add_del_ingress_qdisc(ifindex, false, block_id);
> +    tc_add_del_ingress_qdisc(ifindex, false, block_id, false);
>  }
>  
>  static void
> @@ -1500,12 +1500,12 @@ probe_tc_block_support(int ifindex)
>      uint32_t block_id = 1;
>      int error;
>  
> -    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
> +    error = tc_add_del_ingress_qdisc(ifindex, true, block_id, false);
>      if (error) {
>          return;
>      }
>  
> -    tc_add_del_ingress_qdisc(ifindex, false, block_id);
> +    tc_add_del_ingress_qdisc(ifindex, false, block_id, false);
>  
>      block_support = true;
>      VLOG_INFO("probe tc: block offload is supported.");
> @@ -1538,7 +1538,7 @@ netdev_tc_init_flow_api(struct netdev *netdev)
>      }
>  
>      block_id = get_block_id_from_netdev(netdev);
> -    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
> +    error = tc_add_del_ingress_qdisc(ifindex, true, block_id, false);
>  
>      if (error && error != EEXIST) {
>          VLOG_ERR("failed adding ingress qdisc required for offloading: %s",
> diff --git a/lib/tc.c b/lib/tc.c
> index 2d42003..441115a 100644
> --- a/lib/tc.c
> +++ b/lib/tc.c
> @@ -219,7 +219,7 @@ tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
>   * Returns 0 if successful, otherwise a positive errno value.
>   */
>  int
> -tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id)
> +tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id, bool egress)
>  {
>      struct ofpbuf request;
>      struct tcmsg *tcmsg;
> @@ -228,11 +228,19 @@ tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id)
>      int flags = add ? NLM_F_EXCL | NLM_F_CREATE : 0;
>  
>      tcmsg = tc_make_request(ifindex, type, flags, &request);
> -    tcmsg->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
> -    tcmsg->tcm_parent = TC_H_INGRESS;
> -    nl_msg_put_string(&request, TCA_KIND, "ingress");
> +
> +    if (egress) {
> +        tcmsg->tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
> +        tcmsg->tcm_parent = TC_H_CLSACT;
> +        nl_msg_put_string(&request, TCA_KIND, "clsact");
> +    } else {
> +        tcmsg->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
> +        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) {
> +    if (!egress && block_id) {
>          nl_msg_put_u32(&request, TCA_INGRESS_BLOCK, block_id);
>      }
>  
> @@ -1441,7 +1449,8 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower)
>  }
>  
>  int
> -tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
> +tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id,
> +                     bool egress)
>  {
>      struct ofpbuf request;
>      struct tcmsg *tcmsg;
> @@ -1449,7 +1458,8 @@ tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
>  
>      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_parent =
> +        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
>      tcmsg->tcm_info = TC_H_UNSPEC;
>      tcmsg->tcm_handle = 0;
>  
> @@ -1460,7 +1470,7 @@ tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
>  }
>  
>  int
> -tc_flush(int ifindex, uint32_t block_id)
> +tc_flush(int ifindex, uint32_t block_id, bool egress)
>  {
>      struct ofpbuf request;
>      struct tcmsg *tcmsg;
> @@ -1468,14 +1478,16 @@ tc_flush(int ifindex, uint32_t block_id)
>  
>      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_parent =
> +        egress ? TC_EGRESS_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, uint32_t block_id)
> +tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id,
> +              bool egress)
>  {
>      struct ofpbuf request;
>      struct tcmsg *tcmsg;
> @@ -1485,7 +1497,8 @@ tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id)
>  
>      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_parent =
> +        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
>      tcmsg->tcm_info = tc_make_handle(prio, 0);
>      tcmsg->tcm_handle = handle;
>  
> @@ -1498,7 +1511,7 @@ 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,
> -              uint32_t block_id)
> +              uint32_t block_id, bool egress)
>  {
>      struct ofpbuf request;
>      struct tcmsg *tcmsg;
> @@ -1508,7 +1521,8 @@ tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower,
>  
>      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_parent =
> +        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
>      tcmsg->tcm_info = tc_make_handle(prio, 0);
>      tcmsg->tcm_handle = handle;
>  
> @@ -2228,7 +2242,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, uint32_t block_id)
> +                  struct tc_flower *flower, uint32_t block_id, bool egress)
>  {
>      struct ofpbuf request;
>      struct tcmsg *tcmsg;
> @@ -2241,7 +2255,8 @@ tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
>      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_parent =
> +        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
>      tcmsg->tcm_info = tc_make_handle(prio, eth_type);
>      tcmsg->tcm_handle = handle;
>  
> diff --git a/lib/tc.h b/lib/tc.h
> index 3113206..adc79cf 100644
> --- a/lib/tc.h
> +++ b/lib/tc.h
> @@ -36,8 +36,12 @@
>  #ifndef TC_H_MIN_INGRESS
>  #define TC_H_MIN_INGRESS       0xFFF2U
>  #endif
> +#ifndef TC_H_MIN_EGRESS
> +#define TC_H_MIN_EGRESS       0xFFF3U
> +#endif
>  
>  #define TC_INGRESS_PARENT TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS)
> +#define TC_EGRESS_PARENT TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS)
>  
>  #define TC_POLICY_DEFAULT "none"
>  
> @@ -65,7 +69,8 @@ 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, uint32_t block_id);
> +int tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id,
> +                             bool egress);
>  
>  struct tc_cookie {
>      const void *data;
> @@ -215,12 +220,15 @@ 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, uint32_t block_id);
> -int tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id);
> +                      struct tc_flower *flower, uint32_t block_id,
> +                      bool egress);
> +int tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id,
> +                  bool egress);
>  int tc_get_flower(int ifindex, int prio, int handle,
> -                  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);
> +                  struct tc_flower *flower, uint32_t block_id, bool egress);
> +int tc_flush(int ifindex, uint32_t block_id, bool egress);
> +int tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id,
> +                         bool egress);
>  int parse_netlink_to_tc_flower(struct ofpbuf *reply,
>                                 struct tc_flower *flower);
>  void tc_set_policy(const char *policy);
>
John Hurley March 5, 2019, 11:08 p.m. UTC | #2
On Tue, Mar 5, 2019 at 2:12 PM Roi Dayan <roid@mellanox.com> wrote:
>
>
>
> On 27/02/2019 20:28, John Hurley wrote:
> > Offloading rules to a TC datapath only allows the creating of ingress hook
> > qdiscs and the application of filters to these. However, there may be
> > certain situations where an egress qdisc is more applicable (e.g. when
> > offloading to TC rules applied to OvS internal ports).
> >
> > Extend the TC API in OvS to allow the creation of egress qdisc and to add
> > or interact with flower filters applied to these.
> >
> > Signed-off-by: John Hurley <john.hurley@netronome.com>
> > Reviewed-by: Simon Horman <simon.horman@netronome.com>
> > ---
> >  lib/netdev-linux.c       |  8 ++++----
> >  lib/netdev-tc-offloads.c | 30 +++++++++++++++---------------
> >  lib/tc.c                 | 45 ++++++++++++++++++++++++++++++---------------
> >  lib/tc.h                 | 20 ++++++++++++++------
> >  4 files changed, 63 insertions(+), 40 deletions(-)
> >
> > diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> > index 25d037c..8d37eb6 100644
> > --- a/lib/netdev-linux.c
> > +++ b/lib/netdev-linux.c
> > @@ -740,7 +740,7 @@ netdev_linux_update_lag(struct rtnetlink_change *change)
> >
> >                  /* LAG master is linux netdev so add slave to same block. */
> >                  error = tc_add_del_ingress_qdisc(change->if_index, true,
> > -                                                 block_id);
> > +                                                 block_id, false);
> >                  if (error) {
> >                      VLOG_WARN("failed to bind LAG slave to master's block");
> >                      shash_delete(&lag_shash, lag->node);
> > @@ -756,7 +756,7 @@ netdev_linux_update_lag(struct rtnetlink_change *change)
> >
> >          if (lag) {
> >              tc_add_del_ingress_qdisc(change->if_index, false,
> > -                                     lag->block_id);
> > +                                     lag->block_id, false);
> >              shash_delete(&lag_shash, lag->node);
> >              free(lag);
> >          }
> > @@ -2370,7 +2370,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, 0);
> > +    error = tc_add_del_ingress_qdisc(ifindex, false, 0, false);
> >      if (error) {
> >          VLOG_WARN_RL(&rl, "%s: removing policing failed: %s",
> >                       netdev_name, ovs_strerror(error));
> > @@ -2378,7 +2378,7 @@ netdev_linux_set_policing(struct netdev *netdev_,
> >      }
> >
> >      if (kbits_rate) {
> > -        error = tc_add_del_ingress_qdisc(ifindex, true, 0);
> > +        error = tc_add_del_ingress_qdisc(ifindex, true, 0, false);
> >          if (error) {
> >              VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s",
> >                           netdev_name, ovs_strerror(error));
>
> you are missing a call in tc_del_matchall_policer
>
> lib/netdev-linux.c: In function ‘tc_del_matchall_policer’:
> lib/netdev-linux.c:2426:11: error: too few arguments to function ‘tc_del_filter’
>      err = tc_del_filter(ifindex, TC_RESERVED_PRIORITY_POLICE, 1, block_id);
>            ^~~~~~~~~~~~~
>

Apologies.
This is somehow in the version I am running on my test machine but
seems to be missing from the patches submitted.
My mistake, thanks.

>
>
> > diff --git a/lib/netdev-tc-offloads.c b/lib/netdev-tc-offloads.c
> > index 36bce15..31ad9f4 100644
> > --- a/lib/netdev-tc-offloads.c
> > +++ b/lib/netdev-tc-offloads.c
> > @@ -182,7 +182,7 @@ del_filter_and_ufid_mapping(int ifindex, int prio, int handle,
> >  {
> >      int err;
> >
> > -    err = tc_del_filter(ifindex, prio, handle, block_id);
> > +    err = tc_del_filter(ifindex, prio, handle, block_id, false);
> >      del_ufid_tc_mapping(ufid);
> >
> >      return err;
> > @@ -350,7 +350,7 @@ netdev_tc_flow_flush(struct netdev *netdev)
> >
> >      block_id = get_block_id_from_netdev(netdev);
> >
> > -    return tc_flush(ifindex, block_id);
> > +    return tc_flush(ifindex, block_id, false);
> >  }
> >
> >  int
> > @@ -372,7 +372,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, block_id);
> > +    tc_dump_flower_start(ifindex, dump->nl_dump, block_id, false);
> >
> >      *dump_out = dump;
> >
> > @@ -1347,7 +1347,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, block_id);
> > +    err = tc_replace_flower(ifindex, prio, handle, &flower, block_id, false);
> >      if (!err) {
> >          add_ufid_tc_mapping(ufid, flower.prio, flower.handle, netdev, ifindex);
> >      }
> > @@ -1390,7 +1390,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);
> >      block_id = get_block_id_from_netdev(netdev);
> > -    err = tc_get_flower(ifindex, prio, handle, &flower, block_id);
> > +    err = tc_get_flower(ifindex, prio, handle, &flower, block_id, false);
> >      netdev_close(dev);
> >      if (err) {
> >          VLOG_ERR_RL(&error_rl, "flow get failed (dev %s prio %d handle %d): %s",
> > @@ -1437,7 +1437,7 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
> >
> >      if (stats) {
> >          memset(stats, 0, sizeof *stats);
> > -        if (!tc_get_flower(ifindex, prio, handle, &flower, block_id)) {
> > +        if (!tc_get_flower(ifindex, prio, handle, &flower, block_id, false)) {
> >              stats->n_packets = get_32aligned_u64(&flower.stats.n_packets);
> >              stats->n_bytes = get_32aligned_u64(&flower.stats.n_bytes);
> >              stats->used = flower.lastused;
> > @@ -1458,7 +1458,7 @@ probe_multi_mask_per_prio(int ifindex)
> >      int block_id = 0;
> >      int error;
> >
> > -    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
> > +    error = tc_add_del_ingress_qdisc(ifindex, true, block_id, false);
> >      if (error) {
> >          return;
> >      }
> > @@ -1470,7 +1470,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, block_id);
> > +    error = tc_replace_flower(ifindex, 1, 1, &flower, block_id, false);
> >      if (error) {
> >          goto out;
> >      }
> > @@ -1478,20 +1478,20 @@ 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, block_id);
> > -    tc_del_filter(ifindex, 1, 1, block_id);
> > +    error = tc_replace_flower(ifindex, 1, 2, &flower, block_id, false);
> > +    tc_del_filter(ifindex, 1, 1, block_id, false);
> >
> >      if (error) {
> >          goto out;
> >      }
> >
> > -    tc_del_filter(ifindex, 1, 2, block_id);
> > +    tc_del_filter(ifindex, 1, 2, block_id, false);
> >
> >      multi_mask_per_prio = true;
> >      VLOG_INFO("probe tc: multiple masks on single tc prio is supported.");
> >
> >  out:
> > -    tc_add_del_ingress_qdisc(ifindex, false, block_id);
> > +    tc_add_del_ingress_qdisc(ifindex, false, block_id, false);
> >  }
> >
> >  static void
> > @@ -1500,12 +1500,12 @@ probe_tc_block_support(int ifindex)
> >      uint32_t block_id = 1;
> >      int error;
> >
> > -    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
> > +    error = tc_add_del_ingress_qdisc(ifindex, true, block_id, false);
> >      if (error) {
> >          return;
> >      }
> >
> > -    tc_add_del_ingress_qdisc(ifindex, false, block_id);
> > +    tc_add_del_ingress_qdisc(ifindex, false, block_id, false);
> >
> >      block_support = true;
> >      VLOG_INFO("probe tc: block offload is supported.");
> > @@ -1538,7 +1538,7 @@ netdev_tc_init_flow_api(struct netdev *netdev)
> >      }
> >
> >      block_id = get_block_id_from_netdev(netdev);
> > -    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
> > +    error = tc_add_del_ingress_qdisc(ifindex, true, block_id, false);
> >
> >      if (error && error != EEXIST) {
> >          VLOG_ERR("failed adding ingress qdisc required for offloading: %s",
> > diff --git a/lib/tc.c b/lib/tc.c
> > index 2d42003..441115a 100644
> > --- a/lib/tc.c
> > +++ b/lib/tc.c
> > @@ -219,7 +219,7 @@ tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
> >   * Returns 0 if successful, otherwise a positive errno value.
> >   */
> >  int
> > -tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id)
> > +tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id, bool egress)
> >  {
> >      struct ofpbuf request;
> >      struct tcmsg *tcmsg;
> > @@ -228,11 +228,19 @@ tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id)
> >      int flags = add ? NLM_F_EXCL | NLM_F_CREATE : 0;
> >
> >      tcmsg = tc_make_request(ifindex, type, flags, &request);
> > -    tcmsg->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
> > -    tcmsg->tcm_parent = TC_H_INGRESS;
> > -    nl_msg_put_string(&request, TCA_KIND, "ingress");
> > +
> > +    if (egress) {
> > +        tcmsg->tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
> > +        tcmsg->tcm_parent = TC_H_CLSACT;
> > +        nl_msg_put_string(&request, TCA_KIND, "clsact");
> > +    } else {
> > +        tcmsg->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
> > +        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) {
> > +    if (!egress && block_id) {
> >          nl_msg_put_u32(&request, TCA_INGRESS_BLOCK, block_id);
> >      }
> >
> > @@ -1441,7 +1449,8 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower)
> >  }
> >
> >  int
> > -tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
> > +tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id,
> > +                     bool egress)
> >  {
> >      struct ofpbuf request;
> >      struct tcmsg *tcmsg;
> > @@ -1449,7 +1458,8 @@ tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
> >
> >      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_parent =
> > +        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
> >      tcmsg->tcm_info = TC_H_UNSPEC;
> >      tcmsg->tcm_handle = 0;
> >
> > @@ -1460,7 +1470,7 @@ tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
> >  }
> >
> >  int
> > -tc_flush(int ifindex, uint32_t block_id)
> > +tc_flush(int ifindex, uint32_t block_id, bool egress)
> >  {
> >      struct ofpbuf request;
> >      struct tcmsg *tcmsg;
> > @@ -1468,14 +1478,16 @@ tc_flush(int ifindex, uint32_t block_id)
> >
> >      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_parent =
> > +        egress ? TC_EGRESS_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, uint32_t block_id)
> > +tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id,
> > +              bool egress)
> >  {
> >      struct ofpbuf request;
> >      struct tcmsg *tcmsg;
> > @@ -1485,7 +1497,8 @@ tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id)
> >
> >      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_parent =
> > +        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
> >      tcmsg->tcm_info = tc_make_handle(prio, 0);
> >      tcmsg->tcm_handle = handle;
> >
> > @@ -1498,7 +1511,7 @@ 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,
> > -              uint32_t block_id)
> > +              uint32_t block_id, bool egress)
> >  {
> >      struct ofpbuf request;
> >      struct tcmsg *tcmsg;
> > @@ -1508,7 +1521,8 @@ tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower,
> >
> >      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_parent =
> > +        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
> >      tcmsg->tcm_info = tc_make_handle(prio, 0);
> >      tcmsg->tcm_handle = handle;
> >
> > @@ -2228,7 +2242,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, uint32_t block_id)
> > +                  struct tc_flower *flower, uint32_t block_id, bool egress)
> >  {
> >      struct ofpbuf request;
> >      struct tcmsg *tcmsg;
> > @@ -2241,7 +2255,8 @@ tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
> >      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_parent =
> > +        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
> >      tcmsg->tcm_info = tc_make_handle(prio, eth_type);
> >      tcmsg->tcm_handle = handle;
> >
> > diff --git a/lib/tc.h b/lib/tc.h
> > index 3113206..adc79cf 100644
> > --- a/lib/tc.h
> > +++ b/lib/tc.h
> > @@ -36,8 +36,12 @@
> >  #ifndef TC_H_MIN_INGRESS
> >  #define TC_H_MIN_INGRESS       0xFFF2U
> >  #endif
> > +#ifndef TC_H_MIN_EGRESS
> > +#define TC_H_MIN_EGRESS       0xFFF3U
> > +#endif
> >
> >  #define TC_INGRESS_PARENT TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS)
> > +#define TC_EGRESS_PARENT TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS)
> >
> >  #define TC_POLICY_DEFAULT "none"
> >
> > @@ -65,7 +69,8 @@ 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, uint32_t block_id);
> > +int tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id,
> > +                             bool egress);
> >
> >  struct tc_cookie {
> >      const void *data;
> > @@ -215,12 +220,15 @@ 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, uint32_t block_id);
> > -int tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id);
> > +                      struct tc_flower *flower, uint32_t block_id,
> > +                      bool egress);
> > +int tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id,
> > +                  bool egress);
> >  int tc_get_flower(int ifindex, int prio, int handle,
> > -                  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);
> > +                  struct tc_flower *flower, uint32_t block_id, bool egress);
> > +int tc_flush(int ifindex, uint32_t block_id, bool egress);
> > +int tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id,
> > +                         bool egress);
> >  int parse_netlink_to_tc_flower(struct ofpbuf *reply,
> >                                 struct tc_flower *flower);
> >  void tc_set_policy(const char *policy);
> >

Patch
diff mbox series

diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 25d037c..8d37eb6 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -740,7 +740,7 @@  netdev_linux_update_lag(struct rtnetlink_change *change)
 
                 /* LAG master is linux netdev so add slave to same block. */
                 error = tc_add_del_ingress_qdisc(change->if_index, true,
-                                                 block_id);
+                                                 block_id, false);
                 if (error) {
                     VLOG_WARN("failed to bind LAG slave to master's block");
                     shash_delete(&lag_shash, lag->node);
@@ -756,7 +756,7 @@  netdev_linux_update_lag(struct rtnetlink_change *change)
 
         if (lag) {
             tc_add_del_ingress_qdisc(change->if_index, false,
-                                     lag->block_id);
+                                     lag->block_id, false);
             shash_delete(&lag_shash, lag->node);
             free(lag);
         }
@@ -2370,7 +2370,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, 0);
+    error = tc_add_del_ingress_qdisc(ifindex, false, 0, false);
     if (error) {
         VLOG_WARN_RL(&rl, "%s: removing policing failed: %s",
                      netdev_name, ovs_strerror(error));
@@ -2378,7 +2378,7 @@  netdev_linux_set_policing(struct netdev *netdev_,
     }
 
     if (kbits_rate) {
-        error = tc_add_del_ingress_qdisc(ifindex, true, 0);
+        error = tc_add_del_ingress_qdisc(ifindex, true, 0, false);
         if (error) {
             VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s",
                          netdev_name, ovs_strerror(error));
diff --git a/lib/netdev-tc-offloads.c b/lib/netdev-tc-offloads.c
index 36bce15..31ad9f4 100644
--- a/lib/netdev-tc-offloads.c
+++ b/lib/netdev-tc-offloads.c
@@ -182,7 +182,7 @@  del_filter_and_ufid_mapping(int ifindex, int prio, int handle,
 {
     int err;
 
-    err = tc_del_filter(ifindex, prio, handle, block_id);
+    err = tc_del_filter(ifindex, prio, handle, block_id, false);
     del_ufid_tc_mapping(ufid);
 
     return err;
@@ -350,7 +350,7 @@  netdev_tc_flow_flush(struct netdev *netdev)
 
     block_id = get_block_id_from_netdev(netdev);
 
-    return tc_flush(ifindex, block_id);
+    return tc_flush(ifindex, block_id, false);
 }
 
 int
@@ -372,7 +372,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, block_id);
+    tc_dump_flower_start(ifindex, dump->nl_dump, block_id, false);
 
     *dump_out = dump;
 
@@ -1347,7 +1347,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, block_id);
+    err = tc_replace_flower(ifindex, prio, handle, &flower, block_id, false);
     if (!err) {
         add_ufid_tc_mapping(ufid, flower.prio, flower.handle, netdev, ifindex);
     }
@@ -1390,7 +1390,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);
     block_id = get_block_id_from_netdev(netdev);
-    err = tc_get_flower(ifindex, prio, handle, &flower, block_id);
+    err = tc_get_flower(ifindex, prio, handle, &flower, block_id, false);
     netdev_close(dev);
     if (err) {
         VLOG_ERR_RL(&error_rl, "flow get failed (dev %s prio %d handle %d): %s",
@@ -1437,7 +1437,7 @@  netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
 
     if (stats) {
         memset(stats, 0, sizeof *stats);
-        if (!tc_get_flower(ifindex, prio, handle, &flower, block_id)) {
+        if (!tc_get_flower(ifindex, prio, handle, &flower, block_id, false)) {
             stats->n_packets = get_32aligned_u64(&flower.stats.n_packets);
             stats->n_bytes = get_32aligned_u64(&flower.stats.n_bytes);
             stats->used = flower.lastused;
@@ -1458,7 +1458,7 @@  probe_multi_mask_per_prio(int ifindex)
     int block_id = 0;
     int error;
 
-    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
+    error = tc_add_del_ingress_qdisc(ifindex, true, block_id, false);
     if (error) {
         return;
     }
@@ -1470,7 +1470,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, block_id);
+    error = tc_replace_flower(ifindex, 1, 1, &flower, block_id, false);
     if (error) {
         goto out;
     }
@@ -1478,20 +1478,20 @@  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, block_id);
-    tc_del_filter(ifindex, 1, 1, block_id);
+    error = tc_replace_flower(ifindex, 1, 2, &flower, block_id, false);
+    tc_del_filter(ifindex, 1, 1, block_id, false);
 
     if (error) {
         goto out;
     }
 
-    tc_del_filter(ifindex, 1, 2, block_id);
+    tc_del_filter(ifindex, 1, 2, block_id, false);
 
     multi_mask_per_prio = true;
     VLOG_INFO("probe tc: multiple masks on single tc prio is supported.");
 
 out:
-    tc_add_del_ingress_qdisc(ifindex, false, block_id);
+    tc_add_del_ingress_qdisc(ifindex, false, block_id, false);
 }
 
 static void
@@ -1500,12 +1500,12 @@  probe_tc_block_support(int ifindex)
     uint32_t block_id = 1;
     int error;
 
-    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
+    error = tc_add_del_ingress_qdisc(ifindex, true, block_id, false);
     if (error) {
         return;
     }
 
-    tc_add_del_ingress_qdisc(ifindex, false, block_id);
+    tc_add_del_ingress_qdisc(ifindex, false, block_id, false);
 
     block_support = true;
     VLOG_INFO("probe tc: block offload is supported.");
@@ -1538,7 +1538,7 @@  netdev_tc_init_flow_api(struct netdev *netdev)
     }
 
     block_id = get_block_id_from_netdev(netdev);
-    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
+    error = tc_add_del_ingress_qdisc(ifindex, true, block_id, false);
 
     if (error && error != EEXIST) {
         VLOG_ERR("failed adding ingress qdisc required for offloading: %s",
diff --git a/lib/tc.c b/lib/tc.c
index 2d42003..441115a 100644
--- a/lib/tc.c
+++ b/lib/tc.c
@@ -219,7 +219,7 @@  tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
  * Returns 0 if successful, otherwise a positive errno value.
  */
 int
-tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id)
+tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id, bool egress)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
@@ -228,11 +228,19 @@  tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id)
     int flags = add ? NLM_F_EXCL | NLM_F_CREATE : 0;
 
     tcmsg = tc_make_request(ifindex, type, flags, &request);
-    tcmsg->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
-    tcmsg->tcm_parent = TC_H_INGRESS;
-    nl_msg_put_string(&request, TCA_KIND, "ingress");
+
+    if (egress) {
+        tcmsg->tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
+        tcmsg->tcm_parent = TC_H_CLSACT;
+        nl_msg_put_string(&request, TCA_KIND, "clsact");
+    } else {
+        tcmsg->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
+        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) {
+    if (!egress && block_id) {
         nl_msg_put_u32(&request, TCA_INGRESS_BLOCK, block_id);
     }
 
@@ -1441,7 +1449,8 @@  parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower)
 }
 
 int
-tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
+tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id,
+                     bool egress)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
@@ -1449,7 +1458,8 @@  tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
 
     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_parent =
+        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
     tcmsg->tcm_info = TC_H_UNSPEC;
     tcmsg->tcm_handle = 0;
 
@@ -1460,7 +1470,7 @@  tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
 }
 
 int
-tc_flush(int ifindex, uint32_t block_id)
+tc_flush(int ifindex, uint32_t block_id, bool egress)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
@@ -1468,14 +1478,16 @@  tc_flush(int ifindex, uint32_t block_id)
 
     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_parent =
+        egress ? TC_EGRESS_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, uint32_t block_id)
+tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id,
+              bool egress)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
@@ -1485,7 +1497,8 @@  tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id)
 
     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_parent =
+        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
     tcmsg->tcm_info = tc_make_handle(prio, 0);
     tcmsg->tcm_handle = handle;
 
@@ -1498,7 +1511,7 @@  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,
-              uint32_t block_id)
+              uint32_t block_id, bool egress)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
@@ -1508,7 +1521,8 @@  tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower,
 
     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_parent =
+        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
     tcmsg->tcm_info = tc_make_handle(prio, 0);
     tcmsg->tcm_handle = handle;
 
@@ -2228,7 +2242,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, uint32_t block_id)
+                  struct tc_flower *flower, uint32_t block_id, bool egress)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
@@ -2241,7 +2255,8 @@  tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
     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_parent =
+        egress ? TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT);
     tcmsg->tcm_info = tc_make_handle(prio, eth_type);
     tcmsg->tcm_handle = handle;
 
diff --git a/lib/tc.h b/lib/tc.h
index 3113206..adc79cf 100644
--- a/lib/tc.h
+++ b/lib/tc.h
@@ -36,8 +36,12 @@ 
 #ifndef TC_H_MIN_INGRESS
 #define TC_H_MIN_INGRESS       0xFFF2U
 #endif
+#ifndef TC_H_MIN_EGRESS
+#define TC_H_MIN_EGRESS       0xFFF3U
+#endif
 
 #define TC_INGRESS_PARENT TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS)
+#define TC_EGRESS_PARENT TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS)
 
 #define TC_POLICY_DEFAULT "none"
 
@@ -65,7 +69,8 @@  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, uint32_t block_id);
+int tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id,
+                             bool egress);
 
 struct tc_cookie {
     const void *data;
@@ -215,12 +220,15 @@  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, uint32_t block_id);
-int tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id);
+                      struct tc_flower *flower, uint32_t block_id,
+                      bool egress);
+int tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id,
+                  bool egress);
 int tc_get_flower(int ifindex, int prio, int handle,
-                  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);
+                  struct tc_flower *flower, uint32_t block_id, bool egress);
+int tc_flush(int ifindex, uint32_t block_id, bool egress);
+int tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id,
+                         bool egress);
 int parse_netlink_to_tc_flower(struct ofpbuf *reply,
                                struct tc_flower *flower);
 void tc_set_policy(const char *policy);