[ovs-dev] [PATCH v3 1/3] netdev: Dynamic per-port Flow API.

Roi Dayan roid at mellanox.com
Tue May 14 15:16:59 UTC 2019



On 07/05/2019 12:24, Ilya Maximets wrote:
> Current issues with Flow API:
> 
> * OVS calls offloading functions regardless of successful
>   flow API initialization. (ex. on init_flow_api failure)
> * Static initilaization of Flow API for a netdev_class forbids
>   having different offloading types for different instances
>   of netdev with the same netdev_class. (ex. different vports in
>   'system' and 'netdev' datapaths at the same time)
> 
> Solution:
> 
> * Move Flow API from the netdev_class to netdev instance.
> * Make Flow API dynamic, i.e. probe the APIs and choose the
>   suitable one.
> 
> Side effects:
> 
> * Flow API providers localized as possible in their modules.
> * Now we have an ability to make runtime checks. For example,
>   we could check if particular device supports features we
>   need, like if dpdk device supports RSS+MARK action.
> 
> Signed-off-by: Ilya Maximets <i.maximets at samsung.com>
> ---
>  lib/automake.mk               |   2 +-
>  lib/dpdk.c                    |   2 +
>  lib/netdev-dpdk.c             |  24 +++-
>  lib/netdev-dpdk.h             |   3 +
>  lib/netdev-dummy.c            |  24 +++-
>  lib/netdev-linux.c            |   3 -
>  lib/netdev-linux.h            |  10 --
>  lib/netdev-offload-provider.h |  99 +++++++++++++++
>  lib/netdev-provider.h         |  67 +----------
>  lib/netdev-rte-offloads.c     |  40 +++++-
>  lib/netdev-rte-offloads.h     |  41 +------
>  lib/netdev-tc-offloads.c      |  39 ++++--
>  lib/netdev-tc-offloads.h      |  44 -------
>  lib/netdev-vport.c            |   6 +-
>  lib/netdev.c                  | 221 +++++++++++++++++++++++++++-------
>  tests/dpif-netdev.at          |   4 +-
>  tests/ofproto-macros.at       |   1 -
>  17 files changed, 398 insertions(+), 232 deletions(-)
>  create mode 100644 lib/netdev-offload-provider.h
>  delete mode 100644 lib/netdev-tc-offloads.h
> 
> diff --git a/lib/automake.mk b/lib/automake.mk
> index cc5dccf39..c70fda3f8 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -137,6 +137,7 @@ lib_libopenvswitch_la_SOURCES = \
>  	lib/namemap.c \
>  	lib/netdev-dpdk.h \
>  	lib/netdev-dummy.c \
> +	lib/netdev-offload-provider.h \
>  	lib/netdev-provider.h \
>  	lib/netdev-rte-offloads.h \
>  	lib/netdev-vport.c \
> @@ -393,7 +394,6 @@ lib_libopenvswitch_la_SOURCES += \
>  	lib/netdev-linux.c \
>  	lib/netdev-linux.h \
>  	lib/netdev-tc-offloads.c \
> -	lib/netdev-tc-offloads.h \
>  	lib/netlink-conntrack.c \
>  	lib/netlink-conntrack.h \
>  	lib/netlink-notifier.c \
> diff --git a/lib/dpdk.c b/lib/dpdk.c
> index dc6171546..6c6298635 100644
> --- a/lib/dpdk.c
> +++ b/lib/dpdk.c
> @@ -34,6 +34,7 @@
>  #include "dirs.h"
>  #include "fatal-signal.h"
>  #include "netdev-dpdk.h"
> +#include "netdev-rte-offloads.h"
>  #include "openvswitch/dynamic-string.h"
>  #include "openvswitch/vlog.h"
>  #include "ovs-numa.h"
> @@ -442,6 +443,7 @@ dpdk_init__(const struct smap *ovs_other_config)
>  
>      /* Finally, register the dpdk classes */
>      netdev_dpdk_register();
> +    netdev_dpdk_flow_api_register();
>      return true;
>  }
>  
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index 47153dc60..c06c9ef81 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -4204,6 +4204,27 @@ unlock:
>      return err;
>  }
>  
> +bool
> +netdev_dpdk_flow_api_supported(struct netdev *netdev)
> +{
> +    struct netdev_dpdk *dev;
> +    bool ret = false;
> +
> +    if (!is_dpdk_class(netdev->netdev_class)) {
> +        goto out;
> +    }
> +
> +    dev = netdev_dpdk_cast(netdev);
> +    ovs_mutex_lock(&dev->mutex);
> +    if (dev->type == DPDK_DEV_ETH) {
> +        /* TODO: Check if we able to offload some minimal flow. */
> +        ret = true;
> +    }
> +    ovs_mutex_unlock(&dev->mutex);
> +out:
> +    return ret;
> +}
> +
>  int
>  netdev_dpdk_rte_flow_destroy(struct netdev *netdev,
>                               struct rte_flow *rte_flow,
> @@ -4268,8 +4289,7 @@ netdev_dpdk_rte_flow_create(struct netdev *netdev,
>      .get_features = netdev_dpdk_get_features,           \
>      .get_status = netdev_dpdk_get_status,               \
>      .reconfigure = netdev_dpdk_reconfigure,             \
> -    .rxq_recv = netdev_dpdk_rxq_recv,                   \
> -    DPDK_FLOW_OFFLOAD_API
> +    .rxq_recv = netdev_dpdk_rxq_recv
>  
>  static const struct netdev_class dpdk_class = {
>      .type = "dpdk",
> diff --git a/lib/netdev-dpdk.h b/lib/netdev-dpdk.h
> index 9bbb8d8d6..60631c4f0 100644
> --- a/lib/netdev-dpdk.h
> +++ b/lib/netdev-dpdk.h
> @@ -34,6 +34,9 @@ struct rte_flow_action;
>  
>  void netdev_dpdk_register(void);
>  void free_dpdk_buf(struct dp_packet *);
> +
> +bool netdev_dpdk_flow_api_supported(struct netdev *);
> +
>  int
>  netdev_dpdk_rte_flow_destroy(struct netdev *netdev,
>                               struct rte_flow *rte_flow,
> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> index 3f90ffa09..18eed4cf4 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -24,6 +24,7 @@
>  #include "dp-packet.h"
>  #include "dpif-netdev.h"
>  #include "flow.h"
> +#include "netdev-offload-provider.h"
>  #include "netdev-provider.h"
>  #include "netdev-vport.h"
>  #include "odp-util.h"
> @@ -1523,10 +1524,6 @@ exit:
>      return error ? -1 : 0;
>  }
>  
> -#define DUMMY_FLOW_OFFLOAD_API                          \
> -    .flow_put = netdev_dummy_flow_put,                  \
> -    .flow_del = netdev_dummy_flow_del
> -
>  #define NETDEV_DUMMY_CLASS_COMMON                       \
>      .run = netdev_dummy_run,                            \
>      .wait = netdev_dummy_wait,                          \
> @@ -1559,8 +1556,7 @@ exit:
>      .rxq_dealloc = netdev_dummy_rxq_dealloc,            \
>      .rxq_recv = netdev_dummy_rxq_recv,                  \
>      .rxq_wait = netdev_dummy_rxq_wait,                  \
> -    .rxq_drain = netdev_dummy_rxq_drain,                \
> -    DUMMY_FLOW_OFFLOAD_API
> +    .rxq_drain = netdev_dummy_rxq_drain
>  
>  static const struct netdev_class dummy_class = {
>      NETDEV_DUMMY_CLASS_COMMON,
> @@ -1578,6 +1574,20 @@ static const struct netdev_class dummy_pmd_class = {
>      .is_pmd = true,
>      .reconfigure = netdev_dummy_reconfigure
>  };
> +
> +static int
> +netdev_dummy_offloads_init_flow_api(struct netdev *netdev)
> +{
> +    return is_dummy_class(netdev->netdev_class) ? 0 : EOPNOTSUPP;
> +}
> +
> +static const struct netdev_flow_api netdev_dummy_offloads = {
> +    .type = "dummy",
> +    .flow_put = netdev_dummy_flow_put,
> +    .flow_del = netdev_dummy_flow_del,
> +    .init_flow_api = netdev_dummy_offloads_init_flow_api,
> +};
> +
>  
>  /* Helper functions. */
>  
> @@ -2024,5 +2034,7 @@ netdev_dummy_register(enum dummy_level level)
>      netdev_register_provider(&dummy_internal_class);
>      netdev_register_provider(&dummy_pmd_class);
>  
> +    netdev_register_flow_api_provider(&netdev_dummy_offloads);
> +
>      netdev_vport_tunnel_register();
>  }
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index f75d73fd3..e4ea94cf9 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -55,7 +55,6 @@
>  #include "hash.h"
>  #include "openvswitch/hmap.h"
>  #include "netdev-provider.h"
> -#include "netdev-tc-offloads.h"
>  #include "netdev-vport.h"
>  #include "netlink-notifier.h"
>  #include "netlink-socket.h"
> @@ -3321,7 +3320,6 @@ exit:
>  
>  const struct netdev_class netdev_linux_class = {
>      NETDEV_LINUX_CLASS_COMMON,
> -    LINUX_FLOW_OFFLOAD_API,
>      .type = "system",
>      .construct = netdev_linux_construct,
>      .get_stats = netdev_linux_get_stats,
> @@ -3341,7 +3339,6 @@ const struct netdev_class netdev_tap_class = {
>  
>  const struct netdev_class netdev_internal_class = {
>      NETDEV_LINUX_CLASS_COMMON,
> -    LINUX_FLOW_OFFLOAD_API,
>      .type = "internal",
>      .construct = netdev_linux_construct,
>      .get_stats = netdev_internal_get_stats,
> diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h
> index 17ca91201..e1e30f806 100644
> --- a/lib/netdev-linux.h
> +++ b/lib/netdev-linux.h
> @@ -29,14 +29,4 @@ int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
>                                    const char *flag_name, bool enable);
>  int linux_get_ifindex(const char *netdev_name);
>  
> -#define LINUX_FLOW_OFFLOAD_API                          \
> -   .flow_flush = netdev_tc_flow_flush,                  \
> -   .flow_dump_create = netdev_tc_flow_dump_create,      \
> -   .flow_dump_destroy = netdev_tc_flow_dump_destroy,    \
> -   .flow_dump_next = netdev_tc_flow_dump_next,          \
> -   .flow_put = netdev_tc_flow_put,                      \
> -   .flow_get = netdev_tc_flow_get,                      \
> -   .flow_del = netdev_tc_flow_del,                      \
> -   .init_flow_api = netdev_tc_init_flow_api
> -
>  #endif /* netdev-linux.h */
> diff --git a/lib/netdev-offload-provider.h b/lib/netdev-offload-provider.h
> new file mode 100644
> index 000000000..ceeaa50b0
> --- /dev/null
> +++ b/lib/netdev-offload-provider.h
> @@ -0,0 +1,99 @@
> +/*
> + * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc.
> + * Copyright (c) 2019 Samsung Electronics Co.,Ltd.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     https://eur03.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=02%7C01%7Croid%40mellanox.com%7C320331a5f32d437474f908d6d2cdca07%7Ca652971c7d2e4d9ba6a4d149256f461b%7C0%7C0%7C636928178634854039&amp;sdata=%2BVQUoOrR5xFd6smiLUQOIrGGxxHi2FrVoHPkHGQ7hZU%3D&amp;reserved=0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef NETDEV_FLOW_API_PROVIDER_H
> +#define NETDEV_FLOW_API_PROVIDER_H 1
> +
> +#include "flow.h"
> +#include "openvswitch/types.h"
> +#include "packets.h"
> +
> +#ifdef  __cplusplus
> +extern "C" {
> +#endif
> +
> +struct netdev_flow_api {
> +    char *type;
> +    /* Flush all offloaded flows from a netdev.
> +     * Return 0 if successful, otherwise returns a positive errno value. */
> +    int (*flow_flush)(struct netdev *);
> +
> +    /* Flow dumping interface.
> +     *
> +     * This is the back-end for the flow dumping interface described in
> +     * dpif.h.  Please read the comments there first, because this code
> +     * closely follows it.
> +     *
> +     * On success returns 0 and allocates data, on failure returns
> +     * positive errno. */
> +    int (*flow_dump_create)(struct netdev *, struct netdev_flow_dump **dump);
> +    int (*flow_dump_destroy)(struct netdev_flow_dump *);
> +
> +    /* Returns true if there are more flows to dump.
> +     * 'rbuffer' is used as a temporary buffer and needs to be pre allocated
> +     * by the caller.  While there are more flows the same 'rbuffer'
> +     * should be provided. 'wbuffer' is used to store dumped actions and needs
> +     * to be pre allocated by the caller. */
> +    bool (*flow_dump_next)(struct netdev_flow_dump *, struct match *,
> +                           struct nlattr **actions,
> +                           struct dpif_flow_stats *stats,
> +                           struct dpif_flow_attrs *attrs, ovs_u128 *ufid,
> +                           struct ofpbuf *rbuffer, struct ofpbuf *wbuffer);
> +
> +    /* Offload the given flow on netdev.
> +     * To modify a flow, use the same ufid.
> +     * 'actions' are in netlink format, as with struct dpif_flow_put.
> +     * 'info' is extra info needed to offload the flow.
> +     * 'stats' is populated according to the rules set out in the description
> +     * above 'struct dpif_flow_put'.
> +     * Return 0 if successful, otherwise returns a positive errno value. */
> +    int (*flow_put)(struct netdev *, struct match *, struct nlattr *actions,
> +                    size_t actions_len, const ovs_u128 *ufid,
> +                    struct offload_info *info, struct dpif_flow_stats *);
> +
> +    /* Queries a flow specified by ufid on netdev.
> +     * Fills output buffer as 'wbuffer' in flow_dump_next, which
> +     * needs to be be pre allocated.
> +     * Return 0 if successful, otherwise returns a positive errno value. */
> +    int (*flow_get)(struct netdev *, struct match *, struct nlattr **actions,
> +                    const ovs_u128 *ufid, struct dpif_flow_stats *,
> +                    struct dpif_flow_attrs *, struct ofpbuf *wbuffer);
> +
> +    /* Delete a flow specified by ufid from netdev.
> +     * 'stats' is populated according to the rules set out in the description
> +     * above 'struct dpif_flow_del'.
> +     * Return 0 if successful, otherwise returns a positive errno value. */
> +    int (*flow_del)(struct netdev *, const ovs_u128 *ufid,
> +                    struct dpif_flow_stats *);
> +
> +    /* Initializies the netdev flow api.
> +     * Return 0 if successful, otherwise returns a positive errno value. */
> +    int (*init_flow_api)(struct netdev *);
> +};
> +
> +int netdev_register_flow_api_provider(const struct netdev_flow_api *);
> +int netdev_unregister_flow_api_provider(const char *type);
> +
> +#ifdef __linux__
> +extern const struct netdev_flow_api netdev_tc_offloads;
> +#endif
> +
> +#ifdef  __cplusplus
> +}
> +#endif
> +
> +#endif /* NETDEV_FLOW_API_PROVIDER_H */
> diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
> index fb0c27e6e..653854cbd 100644
> --- a/lib/netdev-provider.h
> +++ b/lib/netdev-provider.h
> @@ -23,6 +23,7 @@
>  #include "netdev.h"
>  #include "openvswitch/list.h"
>  #include "ovs-numa.h"
> +#include "ovs-rcu.h"
>  #include "packets.h"
>  #include "seq.h"
>  #include "openvswitch/shash.h"
> @@ -93,6 +94,9 @@ struct netdev {
>      int n_rxq;
>      struct shash_node *node;            /* Pointer to element in global map. */
>      struct ovs_list saved_flags_list; /* Contains "struct netdev_saved_flags". */
> +
> +    /* Functions to control flow offloading. */
> +    OVSRCU_TYPE(const struct netdev_flow_api *) flow_api;
>      struct netdev_hw_info hw_info;	/* offload-capable netdev info */
>  };
>  
> @@ -822,69 +826,6 @@ struct netdev_class {
>      /* Discards all packets waiting to be received from 'rx'. */
>      int (*rxq_drain)(struct netdev_rxq *rx);
>  
> -    /* ## -------------------------------- ## */
> -    /* ## netdev flow offloading functions ## */
> -    /* ## -------------------------------- ## */
> -
> -    /* If a particular netdev class does not support offloading flows,
> -     * all these function pointers must be NULL. */
> -
> -    /* Flush all offloaded flows from a netdev.
> -     * Return 0 if successful, otherwise returns a positive errno value. */
> -    int (*flow_flush)(struct netdev *);
> -
> -    /* Flow dumping interface.
> -     *
> -     * This is the back-end for the flow dumping interface described in
> -     * dpif.h.  Please read the comments there first, because this code
> -     * closely follows it.
> -     *
> -     * On success returns 0 and allocates data, on failure returns
> -     * positive errno. */
> -    int (*flow_dump_create)(struct netdev *, struct netdev_flow_dump **dump);
> -    int (*flow_dump_destroy)(struct netdev_flow_dump *);
> -
> -    /* Returns true if there are more flows to dump.
> -     * 'rbuffer' is used as a temporary buffer and needs to be pre allocated
> -     * by the caller.  While there are more flows the same 'rbuffer'
> -     * should be provided. 'wbuffer' is used to store dumped actions and needs
> -     * to be pre allocated by the caller. */
> -    bool (*flow_dump_next)(struct netdev_flow_dump *, struct match *,
> -                           struct nlattr **actions,
> -                           struct dpif_flow_stats *stats,
> -                           struct dpif_flow_attrs *attrs, ovs_u128 *ufid,
> -                           struct ofpbuf *rbuffer, struct ofpbuf *wbuffer);
> -
> -    /* Offload the given flow on netdev.
> -     * To modify a flow, use the same ufid.
> -     * 'actions' are in netlink format, as with struct dpif_flow_put.
> -     * 'info' is extra info needed to offload the flow.
> -     * 'stats' is populated according to the rules set out in the description
> -     * above 'struct dpif_flow_put'.
> -     * Return 0 if successful, otherwise returns a positive errno value. */
> -    int (*flow_put)(struct netdev *, struct match *, struct nlattr *actions,
> -                    size_t actions_len, const ovs_u128 *ufid,
> -                    struct offload_info *info, struct dpif_flow_stats *);
> -
> -    /* Queries a flow specified by ufid on netdev.
> -     * Fills output buffer as 'wbuffer' in flow_dump_next, which
> -     * needs to be be pre allocated.
> -     * Return 0 if successful, otherwise returns a positive errno value. */
> -    int (*flow_get)(struct netdev *, struct match *, struct nlattr **actions,
> -                    const ovs_u128 *ufid, struct dpif_flow_stats *,
> -                    struct dpif_flow_attrs *, struct ofpbuf *wbuffer);
> -
> -    /* Delete a flow specified by ufid from netdev.
> -     * 'stats' is populated according to the rules set out in the description
> -     * above 'struct dpif_flow_del'.
> -     * Return 0 if successful, otherwise returns a positive errno value. */
> -    int (*flow_del)(struct netdev *, const ovs_u128 *ufid,
> -                    struct dpif_flow_stats *);
> -
> -    /* Initializies the netdev flow api.
> -     * Return 0 if successful, otherwise returns a positive errno value. */
> -    int (*init_flow_api)(struct netdev *);
> -
>      /* Get a block_id from the netdev.
>       * Returns the block_id or 0 if none exists for netdev. */
>      uint32_t (*get_block_id)(struct netdev *);
> diff --git a/lib/netdev-rte-offloads.c b/lib/netdev-rte-offloads.c
> index e9ab08624..683763f43 100644
> --- a/lib/netdev-rte-offloads.c
> +++ b/lib/netdev-rte-offloads.c
> @@ -21,6 +21,7 @@
>  
>  #include "cmap.h"
>  #include "dpif-netdev.h"
> +#include "netdev-offload-provider.h"
>  #include "netdev-provider.h"
>  #include "openvswitch/match.h"
>  #include "openvswitch/vlog.h"
> @@ -29,6 +30,23 @@
>  
>  VLOG_DEFINE_THIS_MODULE(netdev_rte_offloads);
>  
> +/* Thread-safety
> + * =============
> + *
> + * Below API is NOT thread safe in following terms:
> + *
> + *  - The caller must be sure that none of these functions will be called
> + *    simultaneously.  Even for different 'netdev's.
> + *
> + *  - The caller must be sure that 'netdev' will not be destructed/deallocated.
> + *
> + *  - The caller must be sure that 'netdev' configuration will not be changed.
> + *    For example, simultaneous call of 'netdev_reconfigure()' for the same
> + *    'netdev' is forbidden.
> + *
> + * For current implementation all above restrictions could be fulfilled by
> + * taking the datapath 'port_mutex' in lib/dpif-netdev.c.  */
> +
>  /*
>   * A mapping from ufid to dpdk rte_flow.
>   */
> @@ -689,7 +707,7 @@ netdev_rte_offloads_destroy_flow(struct netdev *netdev,
>      return ret;
>  }
>  
> -int
> +static int
>  netdev_rte_offloads_flow_put(struct netdev *netdev, struct match *match,
>                               struct nlattr *actions, size_t actions_len,
>                               const ovs_u128 *ufid, struct offload_info *info,
> @@ -719,7 +737,7 @@ netdev_rte_offloads_flow_put(struct netdev *netdev, struct match *match,
>                                          actions_len, ufid, info);
>  }
>  
> -int
> +static int
>  netdev_rte_offloads_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
>                               struct dpif_flow_stats *stats OVS_UNUSED)
>  {
> @@ -731,3 +749,21 @@ netdev_rte_offloads_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
>  
>      return netdev_rte_offloads_destroy_flow(netdev, ufid, rte_flow);
>  }
> +
> +static int
> +netdev_rte_offloads_init_flow_api(struct netdev *netdev)
> +{
> +    return netdev_dpdk_flow_api_supported(netdev) ? 0 : EOPNOTSUPP;
> +}
> +
> +static const struct netdev_flow_api netdev_dpdk_offloads = {
> +    .type = "dpdk_flow_api",
> +    .flow_put = netdev_rte_offloads_flow_put,
> +    .flow_del = netdev_rte_offloads_flow_del,
> +    .init_flow_api = netdev_rte_offloads_init_flow_api,
> +};
> +
> +void netdev_dpdk_flow_api_register(void)
> +{
> +    netdev_register_flow_api_provider(&netdev_dpdk_offloads);
> +}
> diff --git a/lib/netdev-rte-offloads.h b/lib/netdev-rte-offloads.h
> index 18c8a7558..b9b292114 100644
> --- a/lib/netdev-rte-offloads.h
> +++ b/lib/netdev-rte-offloads.h
> @@ -14,44 +14,9 @@
>   * limitations under the License.
>   */
>  
> -#ifndef NETDEV_VPORT_OFFLOADS_H
> -#define NETDEV_VPORT_OFFLOADS_H 1
> +#ifndef NETDEV_DPDK_OFFLOADS_H
> +#define NETDEV_DPDK_OFFLOADS_H 1
>  
> -#include "openvswitch/types.h"
> -
> -struct netdev;
> -struct match;
> -struct nlattr;
> -struct offload_info;
> -struct dpif_flow_stats;
> -
> -/* Thread-safety
> - * =============
> - *
> - * Below API is NOT thread safe in following terms:
> - *
> - *  - The caller must be sure that none of these functions will be called
> - *    simultaneously.  Even for different 'netdev's.
> - *
> - *  - The caller must be sure that 'netdev' will not be destructed/deallocated.
> - *
> - *  - The caller must be sure that 'netdev' configuration will not be changed.
> - *    For example, simultaneous call of 'netdev_reconfigure()' for the same
> - *    'netdev' is forbidden.
> - *
> - * For current implementation all above restrictions could be fulfilled by
> - * taking the datapath 'port_mutex' in lib/dpif-netdev.c.  */
> -
> -int netdev_rte_offloads_flow_put(struct netdev *netdev, struct match *match,
> -                                 struct nlattr *actions, size_t actions_len,
> -                                 const ovs_u128 *ufid,
> -                                 struct offload_info *info,
> -                                 struct dpif_flow_stats *stats);
> -int netdev_rte_offloads_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
> -                                 struct dpif_flow_stats *stats);
> -
> -#define DPDK_FLOW_OFFLOAD_API                   \
> -    .flow_put = netdev_rte_offloads_flow_put,   \
> -    .flow_del = netdev_rte_offloads_flow_del
> +void netdev_dpdk_flow_api_register(void);
>  
>  #endif /* netdev-rte-offloads.h */
> diff --git a/lib/netdev-tc-offloads.c b/lib/netdev-tc-offloads.c
> index d5c66acc1..b9a4a7302 100644
> --- a/lib/netdev-tc-offloads.c
> +++ b/lib/netdev-tc-offloads.c
> @@ -16,7 +16,6 @@
>   */
>  
>  #include <config.h>
> -#include "netdev-tc-offloads.h"
>  
>  #include <errno.h>
>  #include <linux/if_ether.h>
> @@ -31,6 +30,8 @@
>  #include "openvswitch/util.h"
>  #include "openvswitch/vlog.h"
>  #include "netdev-linux.h"
> +#include "netdev-offload-provider.h"
> +#include "netdev-provider.h"
>  #include "netlink.h"
>  #include "netlink-socket.h"
>  #include "odp-netlink.h"
> @@ -350,7 +351,7 @@ get_block_id_from_netdev(struct netdev *netdev)
>      return 0;
>  }
>  
> -int
> +static int
>  netdev_tc_flow_flush(struct netdev *netdev)
>  {
>      enum tc_qdisc_hook hook = get_tc_qdisc_hook(netdev);
> @@ -368,7 +369,7 @@ netdev_tc_flow_flush(struct netdev *netdev)
>      return tc_flush(ifindex, block_id, hook);
>  }
>  
> -int
> +static int
>  netdev_tc_flow_dump_create(struct netdev *netdev,
>                             struct netdev_flow_dump **dump_out)
>  {
> @@ -395,7 +396,7 @@ netdev_tc_flow_dump_create(struct netdev *netdev,
>      return 0;
>  }
>  
> -int
> +static int
>  netdev_tc_flow_dump_destroy(struct netdev_flow_dump *dump)
>  {
>      nl_dump_done(dump->nl_dump);
> @@ -729,7 +730,7 @@ parse_tc_flower_to_match(struct tc_flower *flower,
>      return 0;
>  }
>  
> -bool
> +static bool
>  netdev_tc_flow_dump_next(struct netdev_flow_dump *dump,
>                           struct match *match,
>                           struct nlattr **actions,
> @@ -1082,7 +1083,7 @@ flower_match_to_tun_opt(struct tc_flower *flower, const struct flow_tnl *tnl,
>      flower->mask.tunnel.metadata.present.len = tnl->metadata.present.len;
>  }
>  
> -int
> +static int
>  netdev_tc_flow_put(struct netdev *netdev, struct match *match,
>                     struct nlattr *actions, size_t actions_len,
>                     const ovs_u128 *ufid, struct offload_info *info,
> @@ -1375,7 +1376,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
>      return err;
>  }
>  
> -int
> +static int
>  netdev_tc_flow_get(struct netdev *netdev OVS_UNUSED,
>                     struct match *match,
>                     struct nlattr **actions,
> @@ -1430,7 +1431,7 @@ netdev_tc_flow_get(struct netdev *netdev OVS_UNUSED,
>      return 0;
>  }
>  
> -int
> +static int
>  netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
>                     const ovs_u128 *ufid,
>                     struct dpif_flow_stats *stats)
> @@ -1550,7 +1551,7 @@ probe_tc_block_support(int ifindex)
>      }
>  }
>  
> -int
> +static int
>  netdev_tc_init_flow_api(struct netdev *netdev)
>  {
>      static struct ovsthread_once multi_mask_once = OVSTHREAD_ONCE_INITIALIZER;
> @@ -1562,8 +1563,8 @@ netdev_tc_init_flow_api(struct netdev *netdev)
>  
>      ifindex = netdev_get_ifindex(netdev);
>      if (ifindex < 0) {
> -        VLOG_ERR_RL(&error_rl, "init: failed to get ifindex for %s: %s",
> -                    netdev_get_name(netdev), ovs_strerror(-ifindex));
> +        VLOG_INFO("init: failed to get ifindex for %s: %s",
> +                  netdev_get_name(netdev), ovs_strerror(-ifindex));
>          return -ifindex;
>      }
>  
> @@ -1584,8 +1585,8 @@ netdev_tc_init_flow_api(struct netdev *netdev)
>      error = tc_add_del_qdisc(ifindex, true, block_id, hook);
>  
>      if (error && error != EEXIST) {
> -        VLOG_ERR("failed adding ingress qdisc required for offloading: %s",
> -                 ovs_strerror(error));
> +        VLOG_INFO("failed adding ingress qdisc required for offloading: %s",
> +                  ovs_strerror(error));
>          return error;
>      }
>  
> @@ -1593,3 +1594,15 @@ netdev_tc_init_flow_api(struct netdev *netdev)
>  
>      return 0;
>  }
> +
> +const struct netdev_flow_api netdev_tc_offloads = {
> +   .type = "linux_tc",
> +   .flow_flush = netdev_tc_flow_flush,
> +   .flow_dump_create = netdev_tc_flow_dump_create,
> +   .flow_dump_destroy = netdev_tc_flow_dump_destroy,
> +   .flow_dump_next = netdev_tc_flow_dump_next,
> +   .flow_put = netdev_tc_flow_put,
> +   .flow_get = netdev_tc_flow_get,
> +   .flow_del = netdev_tc_flow_del,
> +   .init_flow_api = netdev_tc_init_flow_api,
> +};
> diff --git a/lib/netdev-tc-offloads.h b/lib/netdev-tc-offloads.h
> deleted file mode 100644
> index ebd8ca884..000000000
> --- a/lib/netdev-tc-offloads.h
> +++ /dev/null
> @@ -1,44 +0,0 @@
> -/*
> - * Copyright (c) 2016 Mellanox Technologies, Ltd.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     https://eur03.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=02%7C01%7Croid%40mellanox.com%7C320331a5f32d437474f908d6d2cdca07%7Ca652971c7d2e4d9ba6a4d149256f461b%7C0%7C0%7C636928178634854039&amp;sdata=%2BVQUoOrR5xFd6smiLUQOIrGGxxHi2FrVoHPkHGQ7hZU%3D&amp;reserved=0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef NETDEV_TC_OFFLOADS_H
> -#define NETDEV_TC_OFFLOADS_H 1
> -
> -#include "netdev-provider.h"
> -
> -int netdev_tc_flow_flush(struct netdev *);
> -int netdev_tc_flow_dump_create(struct netdev *, struct netdev_flow_dump **);
> -int netdev_tc_flow_dump_destroy(struct netdev_flow_dump *);
> -bool netdev_tc_flow_dump_next(struct netdev_flow_dump *, struct match *,
> -                              struct nlattr **actions,
> -                              struct dpif_flow_stats *,
> -                              struct dpif_flow_attrs *,
> -                              ovs_u128 *ufid,
> -                              struct ofpbuf *rbuffer,
> -                              struct ofpbuf *wbuffer);
> -int netdev_tc_flow_put(struct netdev *, struct match *,
> -                       struct nlattr *actions, size_t actions_len,
> -                       const ovs_u128 *, struct offload_info *,
> -                       struct dpif_flow_stats *);
> -int netdev_tc_flow_get(struct netdev *, struct match *,
> -                       struct nlattr **actions, const ovs_u128 *,
> -                       struct dpif_flow_stats *,
> -                       struct dpif_flow_attrs *, struct ofpbuf *);
> -int netdev_tc_flow_del(struct netdev *, const ovs_u128 *,
> -                        struct dpif_flow_stats *);
> -int netdev_tc_init_flow_api(struct netdev *);
> -
> -#endif /* netdev-tc-offloads.h */
> diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
> index ab591667f..92a256af1 100644
> --- a/lib/netdev-vport.c
> +++ b/lib/netdev-vport.c
> @@ -47,7 +47,6 @@
>  #include "unaligned.h"
>  #include "unixctl.h"
>  #include "openvswitch/vlog.h"
> -#include "netdev-tc-offloads.h"
>  #ifdef __linux__
>  #include "netdev-linux.h"
>  #endif
> @@ -1116,10 +1115,8 @@ netdev_vport_get_ifindex(const struct netdev *netdev_)
>  }
>  
>  #define NETDEV_VPORT_GET_IFINDEX netdev_vport_get_ifindex
> -#define NETDEV_FLOW_OFFLOAD_API , LINUX_FLOW_OFFLOAD_API
>  #else /* !__linux__ */
>  #define NETDEV_VPORT_GET_IFINDEX NULL
> -#define NETDEV_FLOW_OFFLOAD_API
>  #endif /* __linux__ */
>  
>  #define VPORT_FUNCTIONS_COMMON                      \
> @@ -1133,8 +1130,7 @@ netdev_vport_get_ifindex(const struct netdev *netdev_)
>      .get_etheraddr = netdev_vport_get_etheraddr,    \
>      .get_stats = netdev_vport_get_stats,            \
>      .get_pt_mode = netdev_vport_get_pt_mode,        \
> -    .update_flags = netdev_vport_update_flags       \
> -    NETDEV_FLOW_OFFLOAD_API
> +    .update_flags = netdev_vport_update_flags
>  
>  #define TUNNEL_FUNCTIONS_COMMON                     \
>      VPORT_FUNCTIONS_COMMON,                         \
> diff --git a/lib/netdev.c b/lib/netdev.c
> index 7d7ecf6f0..de40f72d8 100644
> --- a/lib/netdev.c
> +++ b/lib/netdev.c
> @@ -39,6 +39,7 @@
>  #include "fatal-signal.h"
>  #include "hash.h"
>  #include "openvswitch/list.h"
> +#include "netdev-offload-provider.h"
>  #include "netdev-provider.h"
>  #include "netdev-vport.h"
>  #include "odp-netlink.h"
> @@ -98,6 +99,22 @@ struct netdev_registered_class {
>  
>  static bool netdev_flow_api_enabled = false;
>  
> +/* Protects 'netdev_flow_apis'.  */
> +static struct ovs_mutex netdev_flow_api_provider_mutex = OVS_MUTEX_INITIALIZER;
> +
> +/* Contains 'struct netdev_registered_flow_api's. */
> +static struct cmap netdev_flow_apis = CMAP_INITIALIZER;
> +
> +struct netdev_registered_flow_api {
> +    struct cmap_node cmap_node; /* In 'netdev_flow_apis', by flow_api->type. */
> +    const struct netdev_flow_api *flow_api;
> +
> +    /* Number of references: one for the flow_api itself and one for every
> +     * instance of the netdev that uses it. */
> +    struct ovs_refcount refcnt;
> +};
> +
> +
>  /* This is set pretty low because we probably won't learn anything from the
>   * additional log messages. */
>  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
> @@ -146,6 +163,8 @@ netdev_initialize(void)
>          netdev_register_provider(&netdev_internal_class);
>          netdev_register_provider(&netdev_tap_class);
>          netdev_vport_tunnel_register();
> +
> +        netdev_register_flow_api_provider(&netdev_tc_offloads);
>  #endif
>  #if defined(__FreeBSD__) || defined(__NetBSD__)
>          netdev_register_provider(&netdev_tap_class);
> @@ -279,6 +298,87 @@ netdev_unregister_provider(const char *type)
>      return error;
>  }
>  
> +static struct netdev_registered_flow_api *
> +netdev_lookup_flow_api(const char *type)
> +{
> +    struct netdev_registered_flow_api *rfa;
> +    CMAP_FOR_EACH_WITH_HASH (rfa, cmap_node, hash_string(type, 0),
> +                             &netdev_flow_apis) {
> +        if (!strcmp(type, rfa->flow_api->type)) {
> +            return rfa;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +/* Registers a new netdev flow api provider. */
> +int
> +netdev_register_flow_api_provider(const struct netdev_flow_api *new_flow_api)
> +    OVS_EXCLUDED(netdev_flow_api_provider_mutex)
> +{
> +    int error = 0;
> +
> +    if (!new_flow_api->init_flow_api) {
> +        VLOG_WARN("attempted to register invalid flow api provider: %s",
> +                   new_flow_api->type);
> +        error = EINVAL;
> +    }
> +
> +    ovs_mutex_lock(&netdev_flow_api_provider_mutex);
> +    if (netdev_lookup_flow_api(new_flow_api->type)) {
> +        VLOG_WARN("attempted to register duplicate flow api provider: %s",
> +                   new_flow_api->type);
> +        error = EEXIST;
> +    } else {
> +        struct netdev_registered_flow_api *rfa;
> +
> +        rfa = xmalloc(sizeof *rfa);
> +        cmap_insert(&netdev_flow_apis, &rfa->cmap_node,
> +                    hash_string(new_flow_api->type, 0));
> +        rfa->flow_api = new_flow_api;
> +        ovs_refcount_init(&rfa->refcnt);
> +        VLOG_DBG("netdev: flow API '%s' registered.", new_flow_api->type);
> +    }
> +    ovs_mutex_unlock(&netdev_flow_api_provider_mutex);
> +
> +    return error;
> +}
> +
> +/* Unregisters a netdev flow api provider.  'type' must have been previously
> + * registered and not currently be in use by any netdevs.  After unregistration
> + * netdev flow api of that type cannot be used for netdevs.  (However, the
> + * provider may still be accessible from other threads until the next RCU grace
> + * period, so the caller must not free or re-register the same netdev_flow_api
> + * until that has passed.) */
> +int
> +netdev_unregister_flow_api_provider(const char *type)
> +    OVS_EXCLUDED(netdev_flow_api_provider_mutex)
> +{
> +    struct netdev_registered_flow_api *rfa;
> +    int error;
> +
> +    ovs_mutex_lock(&netdev_flow_api_provider_mutex);
> +    rfa = netdev_lookup_flow_api(type);
> +    if (!rfa) {
> +        VLOG_WARN("attempted to unregister a flow api provider that is not "
> +                  "registered: %s", type);
> +        error = EAFNOSUPPORT;
> +    } else if (ovs_refcount_unref(&rfa->refcnt) != 1) {
> +        ovs_refcount_ref(&rfa->refcnt);
> +        VLOG_WARN("attempted to unregister in use flow api provider: %s",
> +                  type);
> +        error = EBUSY;
> +    } else  {
> +        cmap_remove(&netdev_flow_apis, &rfa->cmap_node,
> +                    hash_string(rfa->flow_api->type, 0));
> +        ovsrcu_postpone(free, rfa);
> +        error = 0;
> +    }
> +    ovs_mutex_unlock(&netdev_flow_api_provider_mutex);
> +
> +    return error;
> +}
> +
>  /* Clears 'types' and enumerates the types of all currently registered netdev
>   * providers into it.  The caller must first initialize the sset. */
>  void
> @@ -414,6 +514,7 @@ netdev_open(const char *name, const char *type, struct netdev **netdevp)
>                  netdev->reconfigure_seq = seq_create();
>                  netdev->last_reconfigure_seq =
>                      seq_read(netdev->reconfigure_seq);
> +                ovsrcu_set(&netdev->flow_api, NULL);
>                  netdev->hw_info.oor = false;
>                  netdev->node = shash_add(&netdev_shash, name, netdev);
>  
> @@ -562,6 +663,8 @@ netdev_unref(struct netdev *dev)
>      ovs_assert(dev->ref_cnt);
>      if (!--dev->ref_cnt) {
>          const struct netdev_class *class = dev->netdev_class;
> +        const struct netdev_flow_api *flow_api =
> +            ovsrcu_get(const struct netdev_flow_api *, &dev->flow_api);
>          struct netdev_registered_class *rc;
>  
>          dev->netdev_class->destruct(dev);
> @@ -576,6 +679,12 @@ netdev_unref(struct netdev *dev)
>  
>          rc = netdev_lookup_class(class->type);
>          ovs_refcount_unref(&rc->refcnt);
> +
> +        if (flow_api) {
> +            struct netdev_registered_flow_api *rfa =
> +                                    netdev_lookup_flow_api(flow_api->type);
> +            ovs_refcount_unref(&rfa->refcnt);
> +        }
>      } else {
>          ovs_mutex_unlock(&netdev_mutex);
>      }
> @@ -2148,34 +2257,58 @@ netdev_reconfigure(struct netdev *netdev)
>              : EOPNOTSUPP);
>  }
>  
> +static int
> +netdev_assign_flow_api(struct netdev *netdev)
> +{
> +    struct netdev_registered_flow_api *rfa;
> +
> +    CMAP_FOR_EACH (rfa, cmap_node, &netdev_flow_apis) {
> +        if (!rfa->flow_api->init_flow_api(netdev)) {
> +            ovs_refcount_ref(&rfa->refcnt);
> +            ovsrcu_set(&netdev->flow_api, rfa->flow_api);
> +            VLOG_INFO("%s: Assigned flow API '%s'.",
> +                      netdev_get_name(netdev), rfa->flow_api->type);
> +            return 0;
> +        }
> +        VLOG_DBG("%s: flow API '%s' is not suitable.",
> +                 netdev_get_name(netdev), rfa->flow_api->type);
> +    }
> +    VLOG_INFO("%s: No suitable flow API found.", netdev_get_name(netdev));
> +
> +    return -1;
> +}
> +
>  int
>  netdev_flow_flush(struct netdev *netdev)
>  {
> -    const struct netdev_class *class = netdev->netdev_class;
> +    const struct netdev_flow_api *flow_api =
> +        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>  
> -    return (class->flow_flush
> -            ? class->flow_flush(netdev)
> -            : EOPNOTSUPP);
> +    return (flow_api && flow_api->flow_flush)
> +           ? flow_api->flow_flush(netdev)
> +           : EOPNOTSUPP;
>  }
>  
>  int
>  netdev_flow_dump_create(struct netdev *netdev, struct netdev_flow_dump **dump)
>  {
> -    const struct netdev_class *class = netdev->netdev_class;
> +    const struct netdev_flow_api *flow_api =
> +        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>  
> -    return (class->flow_dump_create
> -            ? class->flow_dump_create(netdev, dump)
> -            : EOPNOTSUPP);
> +    return (flow_api && flow_api->flow_dump_create)
> +           ? flow_api->flow_dump_create(netdev, dump)
> +           : EOPNOTSUPP;
>  }
>  
>  int
>  netdev_flow_dump_destroy(struct netdev_flow_dump *dump)
>  {
> -    const struct netdev_class *class = dump->netdev->netdev_class;
> +    const struct netdev_flow_api *flow_api =
> +        ovsrcu_get(const struct netdev_flow_api *, &dump->netdev->flow_api);
>  
> -    return (class->flow_dump_destroy
> -            ? class->flow_dump_destroy(dump)
> -            : EOPNOTSUPP);
> +    return (flow_api && flow_api->flow_dump_destroy)
> +           ? flow_api->flow_dump_destroy(dump)
> +           : EOPNOTSUPP;
>  }
>  
>  bool
> @@ -2184,12 +2317,13 @@ netdev_flow_dump_next(struct netdev_flow_dump *dump, struct match *match,
>                        struct dpif_flow_attrs *attrs, ovs_u128 *ufid,
>                        struct ofpbuf *rbuffer, struct ofpbuf *wbuffer)
>  {
> -    const struct netdev_class *class = dump->netdev->netdev_class;
> +    const struct netdev_flow_api *flow_api =
> +        ovsrcu_get(const struct netdev_flow_api *, &dump->netdev->flow_api);
>  
> -    return (class->flow_dump_next
> -            ? class->flow_dump_next(dump, match, actions, stats, attrs,
> -                                    ufid, rbuffer, wbuffer)
> -            : false);
> +    return (flow_api && flow_api->flow_dump_next)
> +           ? flow_api->flow_dump_next(dump, match, actions, stats, attrs,
> +                                      ufid, rbuffer, wbuffer)
> +           : false;
>  }
>  
>  int
> @@ -2198,12 +2332,13 @@ netdev_flow_put(struct netdev *netdev, struct match *match,
>                  const ovs_u128 *ufid, struct offload_info *info,
>                  struct dpif_flow_stats *stats)
>  {
> -    const struct netdev_class *class = netdev->netdev_class;
> +    const struct netdev_flow_api *flow_api =
> +        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>  
> -    return (class->flow_put
> -            ? class->flow_put(netdev, match, actions, act_len, ufid,
> -                              info, stats)
> -            : EOPNOTSUPP);
> +    return (flow_api && flow_api->flow_put)
> +           ? flow_api->flow_put(netdev, match, actions, act_len, ufid,
> +                                info, stats)
> +           : EOPNOTSUPP;
>  }
>  
>  int
> @@ -2212,36 +2347,43 @@ netdev_flow_get(struct netdev *netdev, struct match *match,
>                  struct dpif_flow_stats *stats,
>                  struct dpif_flow_attrs *attrs, struct ofpbuf *buf)
>  {
> -    const struct netdev_class *class = netdev->netdev_class;
> +    const struct netdev_flow_api *flow_api =
> +        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>  
> -    return (class->flow_get
> -            ? class->flow_get(netdev, match, actions, ufid, stats, attrs, buf)
> -            : EOPNOTSUPP);
> +    return (flow_api && flow_api->flow_get)
> +           ? flow_api->flow_get(netdev, match, actions, ufid,
> +                                stats, attrs, buf)
> +           : EOPNOTSUPP;
>  }
>  
>  int
>  netdev_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
>                  struct dpif_flow_stats *stats)
>  {
> -    const struct netdev_class *class = netdev->netdev_class;
> +    const struct netdev_flow_api *flow_api =
> +        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>  
> -    return (class->flow_del
> -            ? class->flow_del(netdev, ufid, stats)
> -            : EOPNOTSUPP);
> +    return (flow_api && flow_api->flow_del)
> +           ? flow_api->flow_del(netdev, ufid, stats)
> +           : EOPNOTSUPP;
>  }
>  
>  int
>  netdev_init_flow_api(struct netdev *netdev)
>  {
> -    const struct netdev_class *class = netdev->netdev_class;
> -
>      if (!netdev_is_flow_api_enabled()) {
>          return EOPNOTSUPP;
>      }
>  
> -    return (class->init_flow_api
> -            ? class->init_flow_api(netdev)
> -            : EOPNOTSUPP);
> +    if (ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api)) {
> +        return 0;
> +    }
> +
> +    if (netdev_assign_flow_api(netdev)) {
> +        return EOPNOTSUPP;
> +    }
> +
> +    return 0;
>  }
>  
>  uint32_t
> @@ -2573,7 +2715,6 @@ netdev_is_offload_rebalance_policy_enabled(void)
>      return netdev_offload_rebalance_policy;
>  }
>  
> -#ifdef __linux__
>  static void
>  netdev_ports_flow_init(void)
>  {
> @@ -2597,8 +2738,10 @@ netdev_set_flow_api_enabled(const struct smap *ovs_other_config)
>  
>              VLOG_INFO("netdev: Flow API Enabled");
>  
> +#ifdef __linux__
>              tc_set_policy(smap_get_def(ovs_other_config, "tc-policy",
>                                         TC_POLICY_DEFAULT));
> +#endif
>  
>              if (smap_get_bool(ovs_other_config, "offload-rebalance", false)) {
>                  netdev_offload_rebalance_policy = true;
> @@ -2610,9 +2753,3 @@ netdev_set_flow_api_enabled(const struct smap *ovs_other_config)
>          }
>      }
>  }
> -#else
> -void
> -netdev_set_flow_api_enabled(const struct smap *ovs_other_config OVS_UNUSED)
> -{
> -}
> -#endif
> diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
> index 039ee971b..af8a29e44 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -361,9 +361,9 @@ AT_CLEANUP
>  
>  m4_define([DPIF_NETDEV_FLOW_HW_OFFLOAD],
>    [AT_SETUP([dpif-netdev - partial hw offload - $1])
> -   AT_SKIP_IF([test "$IS_WIN32" = "yes" || test "$IS_BSD" = "yes"])
>     OVS_VSWITCHD_START(
> -     [add-port br0 p1 -- set interface p1 type=$1 ofport_request=1 options:pstream=punix:$OVS_RUNDIR/p1.sock -- \
> +     [add-port br0 p1 -- \
> +      set interface p1 type=$1 ofport_request=1 options:pstream=punix:$OVS_RUNDIR/p1.sock options:ifindex=1 -- \
>        set bridge br0 datapath-type=dummy \
>                       other-config:datapath-id=1234 fail-mode=secure], [], [],
>        [m4_if([$1], [dummy-pmd], [--dummy-numa="0,0,0,0,1,1,1,1"], [])])
> diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
> index 2f33feabc..d0101b532 100644
> --- a/tests/ofproto-macros.at
> +++ b/tests/ofproto-macros.at
> @@ -350,7 +350,6 @@ m4_define([_OVS_VSWITCHD_START],
>  /ofproto|INFO|datapath ID changed to fedcba9876543210/d
>  /dpdk|INFO|DPDK Disabled - Use other_config:dpdk-init to enable/d
>  /netlink_socket|INFO|netlink: could not enable listening to all nsid/d
> -/netdev: Flow API/d
>  /probe tc:/d
>  /tc: Using policy/d']])
>  ])
> 


Hi,

I did a quick ping test between two ports and after this commit the
command ovs-dpctl dump-flows doesn't show the flows that are in tc.
which did work before the commit.
ovs-appctl dpctl/dump-flows does show the flows fine.

I didn't have time to help debug but I'll try to help tomorrow.

Thanks,
Roi


More information about the dev mailing list